Skip to content

Commit

Permalink
v5.0.6
Browse files Browse the repository at this point in the history
- **PCB Exposure:**
  - (Fix) When importing gerber files via drag and drop to the main window the file was created with 0mm layer height and no exposure set
  - (Fix) Merging multiple gerber files with mirror active was mirroring the image in each draw causing the wrong output (#980)
  - (Fix) Excellon drill format does not load tools when they have spindle parameters [F/C] (#980)
  - (Fix) Excellon drill format to respect the integer and decimal digit count when specifying them (#980)
- **Stress Tower:**
  - (Improvement) Allow to pause and cancel the operation
  - (Improvement) Process layers in a more efficient way to reduce allocations and be able to produce the test without RAM hogging
- (Upgrade) .NET from 9.0.0 to 9.0.1
- (Upgrade) OpenCV from 4.9.0 to 4.10.0
  • Loading branch information
sn4k3 committed Jan 31, 2025
1 parent 27f46ff commit c86fd5d
Show file tree
Hide file tree
Showing 76 changed files with 821 additions and 799 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## 31/01/2025 - v5.0.6

- **PCB Exposure:**
- (Fix) When importing gerber files via drag and drop to the main window the file was created with 0mm layer height and no exposure set
- (Fix) Merging multiple gerber files with mirror active was mirroring the image in each draw causing the wrong output (#980)
- (Fix) Excellon drill format does not load tools when they have spindle parameters [F/C] (#980)
- (Fix) Excellon drill format to respect the integer and decimal digit count when specifying them (#980)
- **Stress Tower:**
- (Improvement) Allow to pause and cancel the operation
- (Improvement) Process layers in a more efficient way to reduce allocations and be able to produce the test without RAM hogging
- (Upgrade) .NET from 9.0.0 to 9.0.1
- (Upgrade) OpenCV from 4.9.0 to 4.10.0

## 09/01/2025 - v5.0.5

- (Add) PrusaSlicer printer: Elegoo Saturn 4 Ultra 16K
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
<AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>

<!-- Versions -->
<UVtoolsVersion>5.0.5</UVtoolsVersion>
<UVtoolsVersion>5.0.6</UVtoolsVersion>
<AvaloniaVersion>11.2.3</AvaloniaVersion>

<!-- MvvmToolkit -->
Expand Down
14 changes: 10 additions & 4 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
- (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)
- **PCB Exposure:**
- (Fix) When importing gerber files via drag and drop to the main window the file was created with 0mm layer height and no exposure set
- (Fix) Merging multiple gerber files with mirror active was mirroring the image in each draw causing the wrong output (#980)
- (Fix) Excellon drill format does not load tools when they have spindle parameters [F/C] (#980)
- (Fix) Excellon drill format to respect the integer and decimal digit count when specifying them (#980)
- **Stress Tower:**
- (Improvement) Allow to pause and cancel the operation
- (Improvement) Process layers in a more efficient way to reduce allocations and be able to produce the test without RAM hogging
- (Upgrade) .NET from 9.0.0 to 9.0.1
- (Upgrade) OpenCV from 4.9.0 to 4.10.0

4 changes: 2 additions & 2 deletions Scripts/UVtools.ScriptSample/ScriptTimelapseSample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ public void ScriptInit()
Script.Version = new Version(0, 1);
Script.MinimumVersionToRun = new Version(3, 0, 0);

InputPositionZ.Value = (float)Math.Round(SlicerFile.PrintHeight + 1, 2);
InputPositionZ.Minimum = (float) Math.Round(SlicerFile.PrintHeight + 0.1, 2);
InputPositionZ.Value = MathF.Round(SlicerFile.PrintHeight + 1, 2);
InputPositionZ.Minimum = MathF.Round(SlicerFile.PrintHeight + 0.1f, 2);
Script.UserInputs.Add(InputPositionZ);
Script.UserInputs.Add(InputRaiseEveryLayerN);
Script.UserInputs.Add(InputWaitTime);
Expand Down
12 changes: 6 additions & 6 deletions UVtools.Core/Converters/SpeedConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,21 @@ public static float Convert(float value, SpeedUnit from, SpeedUnit to, byte roun
SpeedUnit.MillimetersPerSecond => to switch
{
SpeedUnit.MillimetersPerSecond => value,
SpeedUnit.MillimetersPerMinute => (float) Math.Round(value * 60, rounding, MidpointRounding.AwayFromZero),
SpeedUnit.CentimetersPerMinute => (float) Math.Round(value * 6, rounding, MidpointRounding.AwayFromZero),
SpeedUnit.MillimetersPerMinute => MathF.Round(value * 60, rounding, MidpointRounding.AwayFromZero),
SpeedUnit.CentimetersPerMinute => MathF.Round(value * 6, rounding, MidpointRounding.AwayFromZero),
_ => throw new ArgumentOutOfRangeException(nameof(to), to, null)
},
SpeedUnit.MillimetersPerMinute => to switch
{
SpeedUnit.MillimetersPerSecond => (float) Math.Round(value / 60, rounding, MidpointRounding.AwayFromZero),
SpeedUnit.MillimetersPerSecond => MathF.Round(value / 60, rounding, MidpointRounding.AwayFromZero),
SpeedUnit.MillimetersPerMinute => value,
SpeedUnit.CentimetersPerMinute => (float) Math.Round(value / 10, rounding, MidpointRounding.AwayFromZero),
SpeedUnit.CentimetersPerMinute => MathF.Round(value / 10, rounding, MidpointRounding.AwayFromZero),
_ => throw new ArgumentOutOfRangeException(nameof(to), to, null)
},
SpeedUnit.CentimetersPerMinute => to switch
{
SpeedUnit.MillimetersPerSecond => (float) Math.Round(value * (1.0/6.0), rounding, MidpointRounding.AwayFromZero),
SpeedUnit.MillimetersPerMinute => (float)Math.Round(value * 10, rounding, MidpointRounding.AwayFromZero),
SpeedUnit.MillimetersPerSecond => MathF.Round(value * (1.0f/6.0f), rounding, MidpointRounding.AwayFromZero),
SpeedUnit.MillimetersPerMinute => MathF.Round(value * 10, rounding, MidpointRounding.AwayFromZero),
SpeedUnit.CentimetersPerMinute => value,
_ => throw new ArgumentOutOfRangeException(nameof(to), to, null)
},
Expand Down
4 changes: 2 additions & 2 deletions UVtools.Core/Converters/TimeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public static class TimeConverter
/// <param name="value"></param>
/// <param name="rounding"></param>
/// <returns></returns>
public static float SecondsToMilliseconds(float value, byte rounding = 2) => (float)Math.Round(value * 1000f, rounding);
public static float SecondsToMilliseconds(float value, byte rounding = 2) => MathF.Round(value * 1000f, rounding);

/// <summary>
/// Converts seconds to milliseconds
Expand All @@ -34,5 +34,5 @@ public static class TimeConverter
/// <param name="value"></param>
/// <param name="rounding"></param>
/// <returns></returns>
public static float MillisecondsToSeconds(float value, byte rounding = 2) => (float)Math.Round(value / 1000f, rounding);
public static float MillisecondsToSeconds(float value, byte rounding = 2) => MathF.Round(value / 1000f, rounding);
}
6 changes: 3 additions & 3 deletions UVtools.Core/EmguCV/CMat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public float CompressionRatio
var uncompressedLength = UncompressedLength;
if (uncompressedLength == 0 || Length == uncompressedLength) return 1;
if (Length == 0) return uncompressedLength;
return (float)Math.Round((float)uncompressedLength / Length, 2, MidpointRounding.AwayFromZero);
return MathF.Round((float)uncompressedLength / Length, 2, MidpointRounding.AwayFromZero);
}
}

Expand All @@ -156,7 +156,7 @@ public float CompressionPercentage
var uncompressedLength = UncompressedLength;
if (uncompressedLength == 0 || Length == uncompressedLength) return 0;
if (Length == 0) return 100f;
return (float)Math.Round(100 - (Length * 100f / uncompressedLength), 2, MidpointRounding.AwayFromZero);
return MathF.Round(100 - (Length * 100f / uncompressedLength), 2, MidpointRounding.AwayFromZero);
}
}

Expand All @@ -170,7 +170,7 @@ public float CompressionEfficiency
var uncompressedLength = UncompressedLength;
if (uncompressedLength == 0) return 0;
if (Length == 0) return uncompressedLength;
return (float)Math.Round(uncompressedLength * 100f / Length, 2, MidpointRounding.AwayFromZero);
return MathF.Round(uncompressedLength * 100f / Length, 2, MidpointRounding.AwayFromZero);
}
}

Expand Down
75 changes: 61 additions & 14 deletions UVtools.Core/Excellon/ExcellonDrillFormat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using UVtools.Core.Extensions;
Expand All @@ -21,7 +22,7 @@ namespace UVtools.Core.Excellon;
/// <summary>
/// <para>The Excellon drill format is a subset of RS274D and is used by the drilling and routing machines made by the Excellon corporation.
/// Because of Excellon's long history and dominance of the PCB drilling business for many years their format is a defacto industry standard.</para>
/// <para>Almost every PCB layout software can produce this format.However we have noticed that many PCB layout tools do not take
/// <para>Almost every PCB layout software can produce this format. However we have noticed that many PCB layout tools do not take
/// full advantage of the header information which makes reading the drill file more difficult than it should be.</para>
/// <para>https://www.artwork.com/gerber/drl2laser/excellon/index.htm</para>
/// <para>https://gist.github.com/katyo/5692b935abc085b1037e</para>
Expand Down Expand Up @@ -49,7 +50,7 @@ public Tool(uint index, float diameter)

public override string ToString()
{
return $"T{Index}C{nameof(Diameter)}";
return $"T{Index}C{Diameter}";
}
}

Expand Down Expand Up @@ -188,6 +189,8 @@ private void Load(string filePath)
uint selectedToolIndex = 0;

float x = 0, y = 0;
int integerDigits = 0;
int fractionDigits = 0;

while ((line = tr.ReadLine()?.Trim()) is not null)
{
Expand Down Expand Up @@ -224,6 +227,15 @@ private void Load(string filePath)
continue;
}

if (integerDigits == 0 && line.StartsWith(";FILE_FORMAT="))
{
line = line.Remove(0, 13);
var split = line.Split(':', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
if (split.Length < 2) continue;
int.TryParse(split[0], out integerDigits);
int.TryParse(split[0], out fractionDigits);
}

if (line is "ICI" or "ICI,ON")
{
throw new NotImplementedException("ICI (Incremental input of program coordinates) is not yet implemented, please use absolute coordinate system.");
Expand All @@ -246,15 +258,15 @@ private void Load(string filePath)
{
if (!endOfHeader)
{
var match = Regex.Match(line, @"^T([0-9]+)C(([0-9]*[.])?[0-9]+)");
var match = Regex.Match(line, @"^T([0-9]+).*C(([0-9]*[.])?[0-9]+)");
if (match is
{
Success: true,
Groups.Count: >= 4
})
{
var index = uint.Parse(match.Groups[1].Value);
var diameter = float.Parse(match.Groups[2].Value);
var diameter = float.Parse(match.Groups[2].Value, CultureInfo.InvariantCulture);
var tool = new Tool(index, diameter);
Tools.Add(index, tool);
}
Expand All @@ -280,22 +292,40 @@ private void Load(string filePath)
Groups.Count: >= 2
})
{
if (match.Groups[1].Value.Contains('.') || ZerosIncludeType == ExcellonDrillZerosIncludeType.None)
if (match.Groups[1].Value.Contains('.'))
{
x = float.Parse(match.Groups[1].Value);
x = float.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
}
else
{
switch (ZerosIncludeType)
{
case ExcellonDrillZerosIncludeType.None:
case ExcellonDrillZerosIncludeType.Leading:
x = ValueToCoordinate(float.Parse(match.Groups[1].Value.PadRight(PaddingZeros, '0')));
if (integerDigits > 0)
{
var number = match.Groups[1].Value.Insert(integerDigits, ".");
x = float.Parse(number, CultureInfo.InvariantCulture);
}
else
{
x = ValueToCoordinate(float.Parse(match.Groups[1].Value.PadRight(PaddingZeros, '0'), CultureInfo.InvariantCulture));
}
break;
case ExcellonDrillZerosIncludeType.Trail:
x = ValueToCoordinate(float.Parse(match.Groups[1].Value.PadLeft(PaddingZeros, '0')));
if (fractionDigits > 0)
{
var number = match.Groups[1].Value.Insert(match.Groups[1].Value.Length - fractionDigits, ".");
x = float.Parse(number, CultureInfo.InvariantCulture);
}
else
{
x = ValueToCoordinate(float.Parse(match.Groups[1].Value.PadLeft(PaddingZeros, '0'), CultureInfo.InvariantCulture));
}
break;
}
}


}

Expand All @@ -306,19 +336,36 @@ private void Load(string filePath)
Groups.Count: >= 2
})
{
if (match.Groups[1].Value.Contains('.') || ZerosIncludeType == ExcellonDrillZerosIncludeType.None)
if (match.Groups[1].Value.Contains('.'))
{
y = float.Parse(match.Groups[1].Value);
y = float.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
}
else
{
switch (ZerosIncludeType)
{
case ExcellonDrillZerosIncludeType.None:
case ExcellonDrillZerosIncludeType.Leading:
y = ValueToCoordinate(float.Parse(match.Groups[1].Value.PadRight(PaddingZeros, '0')));
if (integerDigits > 0)
{
var number = match.Groups[1].Value.Insert(integerDigits, ".");
y = float.Parse(number, CultureInfo.InvariantCulture);
}
else
{
y = ValueToCoordinate(float.Parse(match.Groups[1].Value.PadRight(PaddingZeros, '0'), CultureInfo.InvariantCulture));
}
break;
case ExcellonDrillZerosIncludeType.Trail:
y = ValueToCoordinate(float.Parse(match.Groups[1].Value.PadLeft(PaddingZeros, '0')));
if (fractionDigits > 0)
{
var number = match.Groups[1].Value.Insert(match.Groups[1].Value.Length - fractionDigits, ".");
y = float.Parse(number, CultureInfo.InvariantCulture);
}
else
{
y = ValueToCoordinate(float.Parse(match.Groups[1].Value.PadLeft(PaddingZeros, '0'), CultureInfo.InvariantCulture));
}
break;
}
}
Expand All @@ -336,8 +383,8 @@ private void Load(string filePath)
public float ValueToCoordinate(float value) =>
UnitType switch
{
ExcellonDrillUnitType.Millimeter => (float) Math.Round(value / MillimeterResolution, PaddingZeros),
ExcellonDrillUnitType.Inch => (float) Math.Round(value / InchResolution, PaddingZeros),
ExcellonDrillUnitType.Millimeter => MathF.Round(value / MillimeterResolution, PaddingZeros),
ExcellonDrillUnitType.Inch => MathF.Round(value / InchResolution, PaddingZeros),
_ => throw new ArgumentOutOfRangeException()
};

Expand Down
5 changes: 1 addition & 4 deletions UVtools.Core/Extensions/EmguExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@
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;
Expand Down Expand Up @@ -1908,7 +1905,7 @@ public static void DrawPolygon(this Mat src, int sides, SizeF diameter, PointF c
{
if (sides == 1)
{
var point1 = center with { X = (float)Math.Round(center.X - diameter.Width / 2, midpointRounding) };
var point1 = center with { X = MathF.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);
Expand Down
4 changes: 2 additions & 2 deletions UVtools.Core/Extensions/RectangleExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ public static Rectangle OffsetBy(this Rectangle src, int x, int y)

public static int Perimeter(this Rectangle src) => src.Width * 2 + src.Height * 2;
public static float Perimeter(this RectangleF src) => src.Width * 2 + src.Height * 2;
public static float Perimeter(this RectangleF src, int round) => (float)Math.Round(src.Perimeter(), round);
public static float Perimeter(this RectangleF src, int round) => MathF.Round(src.Perimeter(), round);
public static int Area(this Rectangle src) => src.Width * src.Height;
public static float Area(this RectangleF src) => src.Width * src.Height;
public static float Area(this RectangleF src, int round) => (float)Math.Round(src.Area(), round);
public static float Area(this RectangleF src, int round) => MathF.Round(src.Area(), round);

/// <summary>
/// Gets the smallest rectangle among all rectangles
Expand Down
Loading

0 comments on commit c86fd5d

Please sign in to comment.