feat(i18n): Add runtime language packs and language switching (#1)

This commit is contained in:
Carm
2026-10-20 09:46:47 +08:00
committed by GitHub
parent 39ed6d7bba
commit 6d48d7f852
52 changed files with 3119 additions and 968 deletions
+20 -23
View File
@@ -5,15 +5,13 @@ using System.Text;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using STPViewer.Services;
using IOPath = System.IO.Path; // CADability.GeoObject.Path(曲線)撞名
using IOPath = System.IO.Path; // Avoid the CADability.GeoObject.Path name collision.
namespace SmokeTest;
/// <summary>
/// STL 匯出驗證(無 UI):
/// 1) binary — 已知立方體網格寫出 → 解析 header/三角形數/座標範圍比對(含縮放)
/// 2) ASCII — facet 數與 solid/endsolid 結構
/// 3) 退化三角形(零面積)自動濾除
/// Headless STL export checks:
/// binary round trip, ASCII facet structure, and degenerate triangle filtering.
/// </summary>
public static class StlExportTest
{
@@ -24,20 +22,20 @@ public static class StlExportTest
string dir = IOPath.Combine(IOPath.GetTempPath(), "stpviewer_stl_test");
Directory.CreateDirectory(dir);
MeshGeometry3D cube = BuildCube(10); // 10×10×10 mm、12 三角形
MeshGeometry3D cube = BuildCube(10); // 10x10x10 mm, 12 triangles.
// 1) binary:三角形數 + 縮放後座標範圍
// Binary triangle count and scaled coordinate range.
{
string f = IOPath.Combine(dir, "cube_bin.stl");
double scale = 1.0 / 25.4; // mm → inch
double scale = 1.0 / 25.4; // mm to inches.
int written = StlExportService.Write(f, new[] { cube }, ascii: false, scale);
(uint count, float min, float max) = ReadBinary(f);
bool ok = written == 12 && count == 12
&& Math.Abs(min - 0f) < 1e-5 && Math.Abs(max - 10f / 25.4f) < 1e-4;
Report(ref failed, ok, $"binary 寫出/回讀 12 三角形、inch 縮放範圍 [{min:F4}, {max:F4}]");
Report(ref failed, ok, $"binary round trip: 12 triangles, inch range [{min:F4}, {max:F4}]");
}
// 2) ASCII:facet 數 + 結構
// ASCII facet count and structure.
{
string f = IOPath.Combine(dir, "cube_ascii.stl");
int written = StlExportService.Write(f, new[] { cube }, ascii: true, scale: 1.0);
@@ -46,15 +44,15 @@ public static class StlExportTest
bool ok = written == 12 && facets == 12
&& text.StartsWith("solid ", StringComparison.Ordinal)
&& text.Contains("endsolid");
Report(ref failed, ok, $"ASCII 結構完整、facet = {facets}");
Report(ref failed, ok, $"ASCII structure valid, facets = {facets}");
}
// 3) 退化三角形濾除:加一個零面積三角形 → 仍是 12
// A zero-area triangle must be filtered from the export.
{
var degenerate = new MeshGeometry3D();
degenerate.Positions.Add(new Point3D(0, 0, 0));
degenerate.Positions.Add(new Point3D(1, 1, 1));
degenerate.Positions.Add(new Point3D(2, 2, 2)); // 共線 → 面積 0
degenerate.Positions.Add(new Point3D(2, 2, 2)); // Collinear: zero area.
degenerate.TriangleIndices.Add(0);
degenerate.TriangleIndices.Add(1);
degenerate.TriangleIndices.Add(2);
@@ -64,17 +62,16 @@ public static class StlExportTest
int written = StlExportService.Write(f, new[] { cube, degenerate }, ascii: false, scale: 1.0);
(uint count, _, _) = ReadBinary(f);
bool ok = written == 12 && count == 12;
Report(ref failed, ok, $"退化三角形濾除(寫出 {written})");
Report(ref failed, ok, $"degenerate triangle filtered ({written} written)");
}
// 4)(可選)真實 STEP:B-rep 精細重新三角化 → 寫 binary STL 驗證
//(不測「較粗」:CADability GetTriangulation 對比快取粗的精度回傳既有快取,粗化無效 → 功能已移除)
// Optional real STEP input: retessellate B-rep geometry at fine precision.
if (stepPath is not null)
{
var data = new StepImportService().Import(stepPath);
var geos = Collect(data.Root).ToList();
int importTris = data.Root.TriangleCount;
Console.WriteLine($" 來源 {IOPath.GetFileName(stepPath)}:{geos.Count} 個 B-rep 物件、匯入網格 {importTris:N0} 三角形");
Console.WriteLine($" Source {IOPath.GetFileName(stepPath)}: {geos.Count} B-rep objects, {importTris:N0} imported triangles");
var meshes = StlExportService.Tessellate(
geos, StlExportService.PrecisionFactor(StlMeshQuality.Fine));
@@ -82,13 +79,13 @@ public static class StlExportTest
int written = StlExportService.Write(f, meshes, ascii: false, scale: 1.0);
(uint count, _, _) = ReadBinary(f);
Report(ref failed, meshes.Count > 0 && written > 0 && count == written,
$"精細重新三角化 {meshes.Count} 面 → {written:N0} 三角形");
// CADability 三角形數對精度非單調(0.4× 反而比 1× 少),Fine=0.15× 實測明顯更細
$"fine retessellation: {meshes.Count} faces, {written:N0} triangles");
// CADability's triangle count is not monotonic in precision; 0.15x is measurably finer.
Report(ref failed, written > importTris,
$"精細({written:N0})> 匯入預設({importTris:N0})");
$"fine ({written:N0}) > imported default ({importTris:N0})");
}
Console.WriteLine(failed == 0 ? "StlExportTest: PASS" : $"StlExportTest: FAIL({failed} 項)");
Console.WriteLine(failed == 0 ? "StlExportTest: PASS" : $"StlExportTest: FAIL ({failed} checks)");
return failed;
}
@@ -106,7 +103,7 @@ public static class StlExportTest
if (!ok) failed++;
}
/// <summary>0..size 的軸對齊立方體,12 個三角形(法向不講究,STL 寫出時自算)</summary>
/// <summary>Axis-aligned cube from 0 to size, with 12 triangles.</summary>
private static MeshGeometry3D BuildCube(double s)
{
var m = new MeshGeometry3D();
@@ -131,7 +128,7 @@ public static class StlExportTest
using var br = new BinaryReader(File.OpenRead(path));
byte[] header = br.ReadBytes(80);
if (Encoding.ASCII.GetString(header, 0, 5) == "solid")
throw new InvalidDataException("binary STL header 不可以 solid 開頭");
throw new InvalidDataException("Binary STL header must not begin with solid");
uint count = br.ReadUInt32();
float min = float.MaxValue, max = float.MinValue;
for (uint t = 0; t < count; t++)