Files
cdsl-cad/backend/tests/test_engine_plane_y_dir_preservation.py
T

120 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""#1 y_dir 静默丢弃:PlaneSpec.from_mapping 与 sketch_solver 必须尊重输入的 y_dir。
中文说明
--------
这个文件在测试什么(issue #1「y_dir 静默丢弃」的回归测试):
1. 背景:SolidWorks 导出草图常带冗余或非正交的 y_dir。
runtime 的 PlaneSpec.from_mappingruntime_types.py:172-180)与
sketch_solver._to_3dsketch_solver.py:43-52)都**无视输入的
y_dir 字段**,永远用 normal × x_dir 叉积补全。后果:
a) 文档作者显式给出的**正交** y_dir 被悄悄替换成叉积结果
(当 x_dir 与 normal 不正交时,重建的 y_dir 与真实几何不符);
b) **偏斜** y_dir 被静默丢弃,没有任何提示,用户不知道
坐标系被正交化过了。
2. 本测试把 y_dir 处理契约固定下来:
- 输入 y_dir 存在且与 x_dir / normal 正交 → **保留**输入值;
- 输入 y_dir 存在但偏斜(非正交)→ 正交化 + **显式 UserWarning**
- 输入 y_dir 缺失 → 用 normal × x_dir 补全(回归护栏);
- sketch_solver 的轮廓点变换(_transform_contours → _to_3d
同样尊重正交的输入 y_dir。
3. sys.path 说明:把 backend/engine 加入搜索路径,直接 import
cdsl_engine 包内模块(与前面几个回归测试风格一致)。
"""
from __future__ import annotations
import sys
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / "backend"))
sys.path.insert(0, str(ROOT / "backend" / "engine"))
from cdsl_engine.runtime_types import PlaneSpec # noqa: E402
from cdsl_engine.sketch_solver import _transform_contours # noqa: E402
def _assert_vector_close(testcase: unittest.TestCase, actual: tuple | list, expected: tuple | list, *, tol: float = 1e-6) -> None:
testcase.assertEqual(len(actual), len(expected))
for left, right in zip(actual, expected):
testcase.assertAlmostEqual(float(left), float(right), delta=tol)
class PlaneYDirPreservationTests(unittest.TestCase):
"""y_dir 处理契约:正交保留 / 偏斜警告 / 缺失补全。"""
def test_orthogonal_input_y_dir_is_preserved(self) -> None:
"""主契约:输入的正交 y_dir 必须被保留,而不是被叉积结果覆盖。
构造一个 x_dir 与 normal 不垂直的 frame(这是 SolidWorks 导出里
最常见的情形):x_dir=(1,0,0)、normal=(0,1,0)。此时
normal × x_dir = (0,0,-1),但文档显式给出 y_dir=(0,0,1)。
修复前:PlaneSpec.y_dir == (0,0,-1)(静默丢弃,红灯)。
修复后:PlaneSpec.y_dir == (0,0,1)(保留输入,绿灯)。
"""
plane = PlaneSpec.from_mapping({
"origin_mm": [0.0, 0.0, 0.0],
"x_dir": [1.0, 0.0, 0.0],
"y_dir": [0.0, 0.0, 1.0],
"normal": [0.0, 1.0, 0.0],
})
_assert_vector_close(self, plane.y_dir, (0.0, 0.0, 1.0))
def test_skewed_input_y_dir_orthogonalizes_and_warns(self) -> None:
"""主契约:偏斜 y_dir 必须被正交化,并且**显式**发出 UserWarning。
输入 y_dir=(0, 1, 1) 与 normal=(0,0,1) 的夹角不是 90°,无法直接
作为右手系 y 轴。修复前:静默替换为 normal × x_dir(无警告,红灯)。
修复后:正交化为 normal × x_dir,同时 raise UserWarning(绿灯)。
"""
with self.assertWarns(UserWarning):
plane = PlaneSpec.from_mapping({
"origin_mm": [0.0, 0.0, 0.0],
"x_dir": [1.0, 0.0, 0.0],
"y_dir": [0.0, 1.0, 1.0],
"normal": [0.0, 0.0, 1.0],
})
# normal × x_dir = (0,0,1) × (1,0,0) = (0,1,0),且为右手系补全
_assert_vector_close(self, plane.y_dir, (0.0, 1.0, 0.0))
def test_missing_y_dir_gets_orthonormal_default(self) -> None:
"""回归护栏:y_dir 字段缺失时,仍用 normal × x_dir 补全。
修复前后都应通过,这条测试防止我们把默认补全路径改坏。
"""
plane = PlaneSpec.from_mapping({
"origin_mm": [0.0, 0.0, 0.0],
"x_dir": [1.0, 0.0, 0.0],
"normal": [0.0, 0.0, 1.0],
})
_assert_vector_close(self, plane.y_dir, (0.0, 1.0, 0.0))
def test_contour_transform_respects_input_y_dir(self) -> None:
"""端到端:sketch_solver 的轮廓点变换必须尊重正交的输入 y_dir。
workplane 与 test_orthogonal_input_y_dir_is_preserved 相同
x=(1,0,0), n=(0,1,0), 输入 y=(0,0,1))。一条从 (0,0) 到 (0,1)
的轮廓直线,修复前 _to_3d 用默认 y_dir=(0,0,-1) 变换,终点落到
(0,0,-1);修复后用输入 y_dir=(0,0,1),终点落到 (0,0,1)。
"""
workplane = {
"origin_mm": [0.0, 0.0, 0.0],
"x_dir": [1.0, 0.0, 0.0],
"y_dir": [0.0, 0.0, 1.0],
"normal": [0.0, 1.0, 0.0],
}
contours = _transform_contours([
{"type": "line", "start_mm": [0.0, 0.0], "end_mm": [0.0, 1.0]},
], workplane)
_assert_vector_close(self, contours[0]["start_mm"], (0.0, 0.0, 0.0))
_assert_vector_close(self, contours[0]["end_mm"], (0.0, 0.0, 1.0))
if __name__ == "__main__":
unittest.main()