feat: 完善 Edge 一级编辑与 CAD 建模语义
This commit is contained in:
@@ -37,6 +37,15 @@ def _specs(info: dict[str, object]) -> list[dict[str, object]]:
|
||||
return specs
|
||||
|
||||
|
||||
def _edge_specs(info: dict[str, object]) -> list[dict[str, object]]:
|
||||
probe = _PropertySpecProbe()
|
||||
probe.selected_kind = "edge"
|
||||
probe.selected_face_id = None
|
||||
probe.selected_edge_id = 7
|
||||
specs, _used = probe._editable_property_specs(info)
|
||||
return specs
|
||||
|
||||
|
||||
def _display_specs(info: dict[str, object]) -> list[dict[str, object]]:
|
||||
probe = _PropertySpecProbe()
|
||||
probe.selected_kind = "face"
|
||||
@@ -80,6 +89,19 @@ def _assert_label(specs: list[dict[str, object]], key: str, label: str) -> None:
|
||||
raise SystemExit(f"{key} label should be {label!r}, got {actual!r}")
|
||||
|
||||
|
||||
def _assert_current_text_contains(
|
||||
specs: list[dict[str, object]],
|
||||
key: str,
|
||||
fragments: tuple[str, ...],
|
||||
label: str,
|
||||
) -> None:
|
||||
spec = _spec(specs, key)
|
||||
text = str(spec.get("current_text") or "")
|
||||
missing = [fragment for fragment in fragments if fragment not in text]
|
||||
if missing:
|
||||
raise SystemExit(f"{label} {key} text missing {missing}: {text!r}")
|
||||
|
||||
|
||||
def _assert_hard_range(specs: list[dict[str, object]], key: str, low: float, high: float) -> None:
|
||||
spec = _spec(specs, key)
|
||||
actual_low = spec.get("min_value")
|
||||
@@ -167,6 +189,8 @@ def _assert_actionable_rows_first(info: dict[str, object], expected_keys: tuple[
|
||||
seen_non_actionable = False
|
||||
front_keys: list[str] = []
|
||||
for spec in display_specs:
|
||||
if bool(spec.get("pin_top")):
|
||||
continue
|
||||
key = str(spec.get("key", ""))
|
||||
if _is_actionable_edit_spec(spec):
|
||||
if seen_non_actionable:
|
||||
@@ -368,6 +392,25 @@ def main() -> int:
|
||||
}
|
||||
plane_specs = _specs(plane_info)
|
||||
plane_keys = {str(spec.get("key", "")) for spec in plane_specs}
|
||||
_assert_label(plane_specs, "cad_modeling_form", "建模形式")
|
||||
_assert_current_text_contains(
|
||||
plane_specs,
|
||||
"cad_modeling_form",
|
||||
("柔性建模", "拉伸切除", "偏移变换"),
|
||||
"plane Face",
|
||||
)
|
||||
_assert_label(plane_specs, "cad_recommended_operation", "推荐操作")
|
||||
_assert_current_text_contains(
|
||||
plane_specs,
|
||||
"cad_recommended_operation",
|
||||
("优先改偏移变换", "拉伸/切除"),
|
||||
"plane Face",
|
||||
)
|
||||
plane_display_specs = _display_specs(plane_info)
|
||||
if str(plane_display_specs[0].get("key", "")) != "cad_modeling_form":
|
||||
raise SystemExit(f"plane Face should show CAD modeling form first: {plane_display_specs[0]}")
|
||||
if str(plane_display_specs[1].get("key", "")) != "cad_recommended_operation":
|
||||
raise SystemExit(f"plane Face should show recommended operation second: {plane_display_specs[1]}")
|
||||
_assert_contains(
|
||||
plane_keys,
|
||||
{
|
||||
@@ -381,7 +424,7 @@ def main() -> int:
|
||||
)
|
||||
_assert_label(plane_specs, "local_face_width", "U向尺寸")
|
||||
_assert_label(plane_specs, "local_face_height", "V向尺寸")
|
||||
_assert_label(plane_specs, "face_target_normal_position", "偏移曲面")
|
||||
_assert_label(plane_specs, "face_target_normal_position", "偏移变换")
|
||||
_assert_actionable_rows_first(
|
||||
plane_info,
|
||||
(
|
||||
@@ -404,6 +447,13 @@ def main() -> int:
|
||||
for fragment in ("Face 区域 1 个", "边界 Edge 4 条", "共享边相邻 Face 4 个"):
|
||||
if fragment not in topology_text:
|
||||
raise SystemExit(f"plane feature topology row should explain first-level counts, got {topology_spec}")
|
||||
_assert_label(plane_feature_rows, "face_edit_semantics", "建模意图")
|
||||
_assert_label(plane_feature_rows, "cad_modeling_form", "建模形式")
|
||||
_assert_label(plane_feature_rows, "cad_recommended_operation", "推荐操作")
|
||||
if str(plane_feature_rows[0].get("key", "")) != "cad_modeling_form":
|
||||
raise SystemExit(f"feature mode should keep CAD modeling form first: {plane_feature_rows[0]}")
|
||||
if str(plane_feature_rows[1].get("key", "")) != "cad_recommended_operation":
|
||||
raise SystemExit(f"feature mode should keep recommended operation second: {plane_feature_rows[1]}")
|
||||
expected_plane_feature_keys = {
|
||||
"area",
|
||||
"local_face_width",
|
||||
@@ -512,24 +562,36 @@ def main() -> int:
|
||||
if shell_owning.get("label") != "缩放特征":
|
||||
raise SystemExit(f"shell thickness owning scope should expose an edit semantic, got {shell_owning}")
|
||||
|
||||
cylinder_keys = _spec_keys(
|
||||
{
|
||||
"surface": "cylinder",
|
||||
"feature_guess": "hole/groove candidate",
|
||||
"diameter": 6.0,
|
||||
"radius": 3.0,
|
||||
"angular_span": 6.283185307179586,
|
||||
"area": 188.0,
|
||||
"area_center": (0.0, 0.0, 0.0),
|
||||
"bbox_center": (0.0, 0.0, 0.0),
|
||||
"axis": (0.0, 0.0, 1.0),
|
||||
"axis_point": (0.0, 0.0, 0.0),
|
||||
"axis_center": (0.0, 0.0, 5.0),
|
||||
"feature_bottom_face_ids": (),
|
||||
}
|
||||
)
|
||||
cylinder_info = {
|
||||
"surface": "cylinder",
|
||||
"feature_guess": "hole/groove candidate",
|
||||
"diameter": 6.0,
|
||||
"radius": 3.0,
|
||||
"angular_span": 6.283185307179586,
|
||||
"area": 188.0,
|
||||
"area_center": (0.0, 0.0, 0.0),
|
||||
"bbox_center": (0.0, 0.0, 0.0),
|
||||
"axis": (0.0, 0.0, 1.0),
|
||||
"axis_point": (0.0, 0.0, 0.0),
|
||||
"axis_center": (0.0, 0.0, 5.0),
|
||||
"feature_bottom_face_ids": (),
|
||||
}
|
||||
cylinder_specs = _specs(cylinder_info)
|
||||
cylinder_keys = {str(spec.get("key", "")) for spec in cylinder_specs}
|
||||
_assert_no_generic_face_leak(cylinder_keys, "cylindrical hole feature")
|
||||
_assert_contains(cylinder_keys, {"diameter", "hole_cylinder_radius"}, "cylindrical hole feature")
|
||||
_assert_current_text_contains(
|
||||
cylinder_specs,
|
||||
"cad_modeling_form",
|
||||
("工程特征", "孔", "重切"),
|
||||
"cylindrical hole feature",
|
||||
)
|
||||
_assert_current_text_contains(
|
||||
cylinder_specs,
|
||||
"cad_recommended_operation",
|
||||
("孔径", "盲孔", "轴心"),
|
||||
"cylindrical hole feature",
|
||||
)
|
||||
|
||||
cylinder_topology_info = {
|
||||
"surface": "cylinder",
|
||||
@@ -646,30 +708,42 @@ def main() -> int:
|
||||
if "slot_chord_width_estimate" in split_full_hole_keys:
|
||||
raise SystemExit(f"split full cylindrical hole should not be shown as a slot: {split_full_hole_keys}")
|
||||
|
||||
slot_keys = _spec_keys(
|
||||
{
|
||||
"surface": "cylinder",
|
||||
"feature_guess": "hole/groove candidate",
|
||||
"diameter": 6.0,
|
||||
"radius": 3.0,
|
||||
"angular_span": 3.141592653589793,
|
||||
"slot_chord_width_estimate": 6.0,
|
||||
"slot_sagitta_depth_estimate": 3.0,
|
||||
"slot_arc_length_estimate": 9.42477796076938,
|
||||
"area": 188.0,
|
||||
"area_center": (0.0, 0.0, 0.0),
|
||||
"bbox_center": (0.0, 0.0, 0.0),
|
||||
"axis": (0.0, 0.0, 1.0),
|
||||
"axis_point": (0.0, 0.0, 0.0),
|
||||
"axis_center": (0.0, 0.0, 5.0),
|
||||
}
|
||||
)
|
||||
slot_info = {
|
||||
"surface": "cylinder",
|
||||
"feature_guess": "hole/groove candidate",
|
||||
"diameter": 6.0,
|
||||
"radius": 3.0,
|
||||
"angular_span": 3.141592653589793,
|
||||
"slot_chord_width_estimate": 6.0,
|
||||
"slot_sagitta_depth_estimate": 3.0,
|
||||
"slot_arc_length_estimate": 9.42477796076938,
|
||||
"area": 188.0,
|
||||
"area_center": (0.0, 0.0, 0.0),
|
||||
"bbox_center": (0.0, 0.0, 0.0),
|
||||
"axis": (0.0, 0.0, 1.0),
|
||||
"axis_point": (0.0, 0.0, 0.0),
|
||||
"axis_center": (0.0, 0.0, 5.0),
|
||||
}
|
||||
slot_specs = _specs(slot_info)
|
||||
slot_keys = {str(spec.get("key", "")) for spec in slot_specs}
|
||||
_assert_no_generic_face_leak(slot_keys, "slot/half-hole feature")
|
||||
_assert_contains(
|
||||
slot_keys,
|
||||
{"slot_chord_width_estimate", "slot_sagitta_depth_estimate", "slot_arc_length_estimate"},
|
||||
"slot/half-hole feature",
|
||||
)
|
||||
_assert_current_text_contains(
|
||||
slot_specs,
|
||||
"cad_modeling_form",
|
||||
("工程特征", "槽", "局部重建"),
|
||||
"slot/half-hole feature",
|
||||
)
|
||||
_assert_current_text_contains(
|
||||
slot_specs,
|
||||
"cad_recommended_operation",
|
||||
("槽宽", "槽深", "轴心"),
|
||||
"slot/half-hole feature",
|
||||
)
|
||||
|
||||
boss_info = {
|
||||
"surface": "cylinder",
|
||||
@@ -691,30 +765,42 @@ def main() -> int:
|
||||
boss_keys = {str(spec.get("key", "")) for spec in boss_specs}
|
||||
_assert_no_generic_face_leak(boss_keys, "boss feature")
|
||||
_assert_contains(boss_keys, {"boss_diameter", "boss_radius", "boss_height"}, "boss feature")
|
||||
_assert_current_text_contains(
|
||||
boss_specs,
|
||||
"cad_modeling_form",
|
||||
("工程特征", "凸台", "局部重建"),
|
||||
"boss feature",
|
||||
)
|
||||
boss_height = _spec(boss_specs, "boss_height")
|
||||
if boss_height.get("scope_default") != "owning":
|
||||
raise SystemExit(f"boss height should default to owning-axis resize: {boss_height}")
|
||||
if boss_height.get("action") != "resize_cylindrical_height_owning_scale":
|
||||
raise SystemExit(f"boss height should use owning-axis action by default: {boss_height}")
|
||||
|
||||
fillet_keys = _spec_keys(
|
||||
{
|
||||
"surface": "cylinder",
|
||||
"feature_guess": "round/fillet candidate",
|
||||
"diameter": 2.0,
|
||||
"radius": 1.0,
|
||||
"angular_span": 1.5707963267948966,
|
||||
"existing_fillet_radius": 1.0,
|
||||
"feature_existing_fillet_support_face_ids": (1, 2),
|
||||
"area": 3.0,
|
||||
"area_center": (0.0, 0.0, 0.0),
|
||||
"bbox_center": (0.0, 0.0, 0.0),
|
||||
"axis": (0.0, 0.0, 1.0),
|
||||
"axis_point": (0.0, 0.0, 0.0),
|
||||
}
|
||||
)
|
||||
fillet_info = {
|
||||
"surface": "cylinder",
|
||||
"feature_guess": "round/fillet candidate",
|
||||
"diameter": 2.0,
|
||||
"radius": 1.0,
|
||||
"angular_span": 1.5707963267948966,
|
||||
"existing_fillet_radius": 1.0,
|
||||
"feature_existing_fillet_support_face_ids": (1, 2),
|
||||
"area": 3.0,
|
||||
"area_center": (0.0, 0.0, 0.0),
|
||||
"bbox_center": (0.0, 0.0, 0.0),
|
||||
"axis": (0.0, 0.0, 1.0),
|
||||
"axis_point": (0.0, 0.0, 0.0),
|
||||
}
|
||||
fillet_specs = _specs(fillet_info)
|
||||
fillet_keys = {str(spec.get("key", "")) for spec in fillet_specs}
|
||||
_assert_no_generic_face_leak(fillet_keys, "existing fillet feature")
|
||||
_assert_contains(fillet_keys, {"existing_fillet_radius_estimate"}, "existing fillet feature")
|
||||
_assert_current_text_contains(
|
||||
fillet_specs,
|
||||
"cad_modeling_form",
|
||||
("工程特征", "倒圆角", "重新倒圆"),
|
||||
"existing fillet feature",
|
||||
)
|
||||
|
||||
analytic_cases = (
|
||||
(
|
||||
@@ -766,6 +852,27 @@ def main() -> int:
|
||||
keys = {str(spec.get("key", "")) for spec in specs}
|
||||
_assert_no_generic_face_leak(keys, label)
|
||||
_assert_contains(keys, required, label)
|
||||
if label == "cone feature":
|
||||
_assert_current_text_contains(
|
||||
specs,
|
||||
"cad_modeling_form",
|
||||
("工程特征", "拔模", "锥孔"),
|
||||
label,
|
||||
)
|
||||
elif label == "sphere feature":
|
||||
_assert_current_text_contains(
|
||||
specs,
|
||||
"cad_modeling_form",
|
||||
("解析曲面", "缩放特征", "球面"),
|
||||
label,
|
||||
)
|
||||
elif label == "torus feature":
|
||||
_assert_current_text_contains(
|
||||
specs,
|
||||
"cad_modeling_form",
|
||||
("解析曲面", "缩放特征", "环面"),
|
||||
label,
|
||||
)
|
||||
if label == "cone feature":
|
||||
_assert_label(specs, "cone_reference_radius", "参考半径")
|
||||
_assert_label(specs, "cone_reference_diameter", "参考直径")
|
||||
@@ -775,6 +882,29 @@ def main() -> int:
|
||||
if surface_spec.get("current_text") != "圆锥面 / 拔模面":
|
||||
raise SystemExit(f"cone surface should be displayed in user-facing Chinese, got {surface_spec}")
|
||||
|
||||
edge_specs = _edge_specs(
|
||||
{
|
||||
"curve": "line",
|
||||
"length": 10.0,
|
||||
"start_point": (0.0, 0.0, 0.0),
|
||||
"end_point": (10.0, 0.0, 0.0),
|
||||
"length_center": (5.0, 0.0, 0.0),
|
||||
}
|
||||
)
|
||||
_assert_current_text_contains(
|
||||
edge_specs,
|
||||
"cad_modeling_form",
|
||||
("柔性建模", "移动几何", "直线 Edge"),
|
||||
"line Edge",
|
||||
)
|
||||
_assert_current_text_contains(
|
||||
edge_specs,
|
||||
"cad_recommended_operation",
|
||||
("优先改长度", "移动端面", "只改当前Edge"),
|
||||
"line Edge",
|
||||
)
|
||||
_assert_contains({str(spec.get("key", "")) for spec in edge_specs}, {"length"}, "line Edge")
|
||||
|
||||
low_recognition_specs = _specs(
|
||||
{
|
||||
"surface": "sphere",
|
||||
|
||||
Reference in New Issue
Block a user