docs: restore Chinese design-rationale comments after i18n merge
PR #1 condensed the existing Chinese comments into shorter English, dropping the 'why / don't revert to X' notes the project relies on (merged-mesh face lookup, Identity-not-null transforms, background section clip, ...). Restore the original comments verbatim where the code is unchanged; new i18n code keeps its English comments. Two FacesContent/MergedContent notes are corrected to the current render modes. Comment-only change (verified: comment-stripped code identical). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
// Three-point alignment checks: known transform, Matrix3D/ModOp agreement, and collinear rejection.
|
||||
// 三點對齊剛體變換數學驗證:
|
||||
// (1) 已知變換還原 (2) Matrix3D ↔ ModOp 兩種表示一致 (3) 共線拒絕
|
||||
using System.Windows.Media.Media3D;
|
||||
using STPViewer.Services;
|
||||
|
||||
@@ -10,29 +11,29 @@ internal static class AlignTest
|
||||
{
|
||||
int failures = 0;
|
||||
|
||||
// Recover a known 90-degree Z rotation followed by translation (5,-3,2).
|
||||
// ── (1) 已知剛體變換:繞 Z 軸 90° + 平移 (5,-3,2),三點求解後應還原同一變換 ──
|
||||
var truth = Matrix3D.Identity;
|
||||
truth.RotateAt(new Quaternion(new Vector3D(0, 0, 1), 90), new Point3D(2, 1, 0));
|
||||
truth.Translate(new Vector3D(5, -3, 2));
|
||||
|
||||
var p1 = new Point3D(0, 0, 0);
|
||||
var p2 = new Point3D(10, 0, 0);
|
||||
var p3 = new Point3D(0, 7, 3); // Not collinear with the first two points.
|
||||
var p3 = new Point3D(0, 7, 3); // 不共線、不在同平面軸上
|
||||
Point3D q1 = truth.Transform(p1), q2 = truth.Transform(p2), q3 = truth.Transform(p3);
|
||||
|
||||
bool ok = RigidAlign.TryRigidTransform(p1, p2, p3, q1, q2, q3, out Matrix3D m);
|
||||
failures += Check("three-point transform solved", ok, true);
|
||||
|
||||
// Corresponding points must coincide.
|
||||
// 三個對應點要精確貼合
|
||||
failures += CheckPoint("p1→q1", m.Transform(p1), q1);
|
||||
failures += CheckPoint("p2→q2", m.Transform(p2), q2);
|
||||
failures += CheckPoint("p3→q3", m.Transform(p3), q3);
|
||||
|
||||
// An independent fourth point checks the full rigid transform.
|
||||
// 剛體變換唯一性:任意第 4 點也應該跟著正確變換
|
||||
var r = new Point3D(-4, 12, 8);
|
||||
failures += CheckPoint("fourth point", m.Transform(r), truth.Transform(r));
|
||||
|
||||
// CADability ModOp and WPF Matrix3D must transform points identically.
|
||||
// ── (2) ToModOp:CADability ModOp 與 WPF Matrix3D 對同一點要算出同樣結果 ──
|
||||
CADability.ModOp op = RigidAlign.ToModOp(m);
|
||||
foreach (Point3D p in new[] { p1, p2, p3, r, new Point3D(1.5, -2.25, 9) })
|
||||
{
|
||||
@@ -47,7 +48,7 @@ internal static class AlignTest
|
||||
}
|
||||
System.Console.WriteLine($" {(failures == 0 ? "PASS" : "----")}: ModOp/Matrix3D agree at five points");
|
||||
|
||||
// Collinear source points must be rejected.
|
||||
// ── (3) 共線拒絕 ──
|
||||
bool collinearRejected = !RigidAlign.TryRigidTransform(
|
||||
new Point3D(0, 0, 0), new Point3D(1, 0, 0), new Point3D(2, 0, 0), // Collinear.
|
||||
q1, q2, q3, out _);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Section clipping checks the retained side and triangle area.
|
||||
// 剖面裁切數學驗證:對單位幾何裁切,檢查保留側與面積守恆
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Media3D;
|
||||
using STPViewer.Services;
|
||||
@@ -11,7 +11,7 @@ internal static class ClipTest
|
||||
{
|
||||
int failures = 0;
|
||||
|
||||
// A single triangle with area 50.
|
||||
// 單一三角形 (0,0,0)(10,0,0)(0,10,0),面積 50
|
||||
var mesh = new MeshGeometry3D
|
||||
{
|
||||
Positions = new Point3DCollection
|
||||
@@ -22,25 +22,25 @@ internal static class ClipTest
|
||||
};
|
||||
mesh.Freeze();
|
||||
|
||||
// A plane at x=20 retains the full triangle.
|
||||
// case 1: 平面 x=20,全保留
|
||||
var m1 = SectionService.ClipMesh(mesh, new Point3D(20, 0, 0), new Vector3D(1, 0, 0));
|
||||
failures += Check("fully retained", Area(m1), 50.0);
|
||||
|
||||
// A plane at x=-1 removes the full triangle.
|
||||
// case 2: 平面 x=-1,全裁掉
|
||||
var m2 = SectionService.ClipMesh(mesh, new Point3D(-1, 0, 0), new Vector3D(1, 0, 0));
|
||||
failures += Check("fully removed", Area(m2), 0.0);
|
||||
|
||||
// At x=5, the retained trapezoid has area 50 - 12.5 = 37.5.
|
||||
// case 3: 平面 x=5 → 保留 x≤5:梯形面積 = 50 - 右側小三角形(底5高5)/2 = 50 - 12.5 = 37.5
|
||||
var m3 = SectionService.ClipMesh(mesh, new Point3D(5, 0, 0), new Vector3D(1, 0, 0));
|
||||
failures += Check("half clipped (x<=5)", Area(m3), 37.5);
|
||||
foreach (Point3D p in m3.Positions)
|
||||
if (p.X > 5 + 1e-9) { Console.WriteLine($" FAIL: vertex x={p.X} is outside retained side"); failures++; }
|
||||
|
||||
// Reverse the plane normal to retain the smaller triangle.
|
||||
// case 4: 反向(保留 x≥5)→ 12.5
|
||||
var m4 = SectionService.ClipMesh(mesh, new Point3D(5, 0, 0), new Vector3D(-1, 0, 0));
|
||||
failures += Check("half clipped (x>=5)", Area(m4), 12.5);
|
||||
|
||||
// Clip a line segment.
|
||||
// case 5: 線段裁切
|
||||
var segs = new Point3DCollection { new(0, 0, 0), new(10, 0, 0) };
|
||||
Point3DCollection s1 = SectionService.ClipSegments(segs, new Point3D(4, 0, 0), new Vector3D(1, 0, 0));
|
||||
double segLen = s1.Count == 2 ? (s1[1] - s1[0]).Length : -1;
|
||||
|
||||
@@ -5,13 +5,13 @@ using System.Linq;
|
||||
using CADability;
|
||||
using CADability.GeoObject;
|
||||
using STPViewer.Services;
|
||||
using IOPath = System.IO.Path; // Avoid the CADability.GeoObject.Path name collision.
|
||||
using IOPath = System.IO.Path; // CADability.GeoObject.Path(曲線)撞名
|
||||
|
||||
namespace SmokeTest;
|
||||
|
||||
/// <summary>
|
||||
/// Headless STEP export round trip: import, export through the same CADability path,
|
||||
/// then import the result and compare solid counts.
|
||||
/// STEP 匯出往返驗證(v0.5.0 匯出 STEP 功能的無 UI 驗證):
|
||||
/// 匯入來源 STEP → 走與 MainViewModel.ExportStepFile 相同路徑寫出新檔 → 重新匯入比對實體數。
|
||||
/// </summary>
|
||||
public static class ExportTest
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Interference checks for intersecting, separated, and touching meshes.
|
||||
// 干涉檢查數學驗證:相交 / 分離 / 貼合三種情境
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Media3D;
|
||||
using STPViewer.Services;
|
||||
@@ -11,14 +11,14 @@ internal static class InterferenceTest
|
||||
{
|
||||
int failures = 0;
|
||||
|
||||
// Two tetrahedra overlap when B is offset by 5 along X.
|
||||
// 兩個互穿的四面體(B 比 A 平移 +5,邊長 10 → 必相交)
|
||||
MeshGeometry3D a = Tetra(new Vector3D(0, 0, 0));
|
||||
MeshGeometry3D b = Tetra(new Vector3D(5, 0, 0));
|
||||
InterferenceResult r1 = InterferenceService.Check(new[] { a }, new[] { b });
|
||||
failures += Check("overlap intersects", r1.Intersects, true);
|
||||
failures += Check("overlap has intersection segments", r1.Segments.Count > 0, true);
|
||||
|
||||
// B at X=30 leaves a gap of about 20.
|
||||
// 分離(B 平移 +30,gap 應 ≈ 30 - 10 = 20)
|
||||
MeshGeometry3D c = Tetra(new Vector3D(30, 0, 0));
|
||||
InterferenceResult r2 = InterferenceService.Check(new[] { a }, new[] { c });
|
||||
failures += Check("separated meshes do not intersect", r2.Intersects, false);
|
||||
@@ -26,7 +26,7 @@ internal static class InterferenceTest
|
||||
Console.WriteLine($" {(gapOk ? "PASS" : "FAIL")}: separated gap = {r2.GapDistance:F4} (expected ~20)");
|
||||
if (!gapOk) failures++;
|
||||
|
||||
// B at X=10 touches a vertex without penetrating A.
|
||||
// 貼合(B 平移 +10,頂點剛好接觸 → 不算穿透、gap ≈ 0)
|
||||
MeshGeometry3D d = Tetra(new Vector3D(10, 0, 0));
|
||||
InterferenceResult r3 = InterferenceService.Check(new[] { a }, new[] { d });
|
||||
failures += Check("touching is not penetration", r3.Intersects, false);
|
||||
@@ -45,7 +45,7 @@ internal static class InterferenceTest
|
||||
return ok ? 0 : 1;
|
||||
}
|
||||
|
||||
/// <summary>A tetrahedron with side length 10, translated by offset.</summary>
|
||||
/// <summary>邊長 10 的四面體,offset 平移</summary>
|
||||
private static MeshGeometry3D Tetra(Vector3D offset)
|
||||
{
|
||||
Point3D P(double x, double y, double z) => new Point3D(x, y, z) + offset;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generate a small standard DXF test file with the bundled netDxf library.
|
||||
// 測試輔助:用 CADability 內嵌的 netDxf 產生標準 DXF 測試檔
|
||||
using netDxf;
|
||||
using netDxf.Entities;
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ using System.IO;
|
||||
|
||||
namespace SmokeTest;
|
||||
|
||||
/// <summary>Generate a binary STL grid with two triangles per cell and noncoplanar vertices.</summary>
|
||||
/// <summary>產生測試用 binary STL:一片 gridN×gridN 的網格,每格 2 個三角形(波浪起伏,避免全共面)。</summary>
|
||||
internal static class MakeStl
|
||||
{
|
||||
public static void Run(string path, int triCount)
|
||||
{
|
||||
// Two triangles per cell; use a square grid large enough for the requested count.
|
||||
// 每格 2 三角,格數 = triCount/2;邊長 grid = ceil(sqrt(cells))
|
||||
int cells = System.Math.Max(1, triCount / 2);
|
||||
int grid = (int)System.Math.Ceiling(System.Math.Sqrt(cells));
|
||||
int tris = grid * grid * 2;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Headless smoke tests for CAD import, triangulation, and the assembly tree.
|
||||
// 無 UI smoke test:驗證 CAD 檔解析 → 三角化 → 裝配樹 整條匯入管線
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using STPViewer.Services;
|
||||
|
||||
@@ -5,13 +5,15 @@ using System.Text;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Media3D;
|
||||
using STPViewer.Services;
|
||||
using IOPath = System.IO.Path; // Avoid the CADability.GeoObject.Path name collision.
|
||||
using IOPath = System.IO.Path; // CADability.GeoObject.Path(曲線)撞名
|
||||
|
||||
namespace SmokeTest;
|
||||
|
||||
/// <summary>
|
||||
/// Headless STL export checks:
|
||||
/// binary round trip, ASCII facet structure, and degenerate triangle filtering.
|
||||
/// STL 匯出驗證(無 UI):
|
||||
/// 1) binary — 已知立方體網格寫出 → 解析 header/三角形數/座標範圍比對(含縮放)
|
||||
/// 2) ASCII — facet 數與 solid/endsolid 結構
|
||||
/// 3) 退化三角形(零面積)自動濾除
|
||||
/// </summary>
|
||||
public static class StlExportTest
|
||||
{
|
||||
@@ -22,9 +24,9 @@ public static class StlExportTest
|
||||
string dir = IOPath.Combine(IOPath.GetTempPath(), "stpviewer_stl_test");
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
MeshGeometry3D cube = BuildCube(10); // 10x10x10 mm, 12 triangles.
|
||||
MeshGeometry3D cube = BuildCube(10); // 10×10×10 mm、12 三角形
|
||||
|
||||
// Binary triangle count and scaled coordinate range.
|
||||
// 1) binary:三角形數 + 縮放後座標範圍
|
||||
{
|
||||
string f = IOPath.Combine(dir, "cube_bin.stl");
|
||||
double scale = 1.0 / 25.4; // mm to inches.
|
||||
@@ -35,7 +37,7 @@ public static class StlExportTest
|
||||
Report(ref failed, ok, $"binary round trip: 12 triangles, inch range [{min:F4}, {max:F4}]");
|
||||
}
|
||||
|
||||
// ASCII facet count and structure.
|
||||
// 2) ASCII:facet 數 + 結構
|
||||
{
|
||||
string f = IOPath.Combine(dir, "cube_ascii.stl");
|
||||
int written = StlExportService.Write(f, new[] { cube }, ascii: true, scale: 1.0);
|
||||
@@ -47,12 +49,12 @@ public static class StlExportTest
|
||||
Report(ref failed, ok, $"ASCII structure valid, facets = {facets}");
|
||||
}
|
||||
|
||||
// A zero-area triangle must be filtered from the export.
|
||||
// 3) 退化三角形濾除:加一個零面積三角形 → 仍是 12
|
||||
{
|
||||
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)); // Collinear: zero area.
|
||||
degenerate.Positions.Add(new Point3D(2, 2, 2)); // 共線 → 面積 0
|
||||
degenerate.TriangleIndices.Add(0);
|
||||
degenerate.TriangleIndices.Add(1);
|
||||
degenerate.TriangleIndices.Add(2);
|
||||
@@ -65,7 +67,8 @@ public static class StlExportTest
|
||||
Report(ref failed, ok, $"degenerate triangle filtered ({written} written)");
|
||||
}
|
||||
|
||||
// Optional real STEP input: retessellate B-rep geometry at fine precision.
|
||||
// 4)(可選)真實 STEP:B-rep 精細重新三角化 → 寫 binary STL 驗證
|
||||
//(不測「較粗」:CADability GetTriangulation 對比快取粗的精度回傳既有快取,粗化無效 → 功能已移除)
|
||||
if (stepPath is not null)
|
||||
{
|
||||
var data = new StepImportService().Import(stepPath);
|
||||
@@ -80,7 +83,7 @@ public static class StlExportTest
|
||||
(uint count, _, _) = ReadBinary(f);
|
||||
Report(ref failed, meshes.Count > 0 && written > 0 && count == written,
|
||||
$"fine retessellation: {meshes.Count} faces, {written:N0} triangles");
|
||||
// CADability's triangle count is not monotonic in precision; 0.15x is measurably finer.
|
||||
// CADability 三角形數對精度非單調(0.4× 反而比 1× 少),Fine=0.15× 實測明顯更細
|
||||
Report(ref failed, written > importTris,
|
||||
$"fine ({written:N0}) > imported default ({importTris:N0})");
|
||||
}
|
||||
@@ -103,7 +106,7 @@ public static class StlExportTest
|
||||
if (!ok) failed++;
|
||||
}
|
||||
|
||||
/// <summary>Axis-aligned cube from 0 to size, with 12 triangles.</summary>
|
||||
/// <summary>0..size 的軸對齊立方體,12 個三角形(法向不講究,STL 寫出時自算)</summary>
|
||||
private static MeshGeometry3D BuildCube(double s)
|
||||
{
|
||||
var m = new MeshGeometry3D();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Print the STEP object hierarchy after import with HierarchyToBlocks enabled.
|
||||
// 實驗工具:印出 STEP 匯入後的物件階層(HierarchyToBlocks=true)
|
||||
using CADability;
|
||||
using CADability.GeoObject;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user