feat: 完善 Face 一级关系编辑和稳定性
This commit is contained in:
@@ -36,6 +36,13 @@ def _specs(info: dict[str, object]) -> list[dict[str, object]]:
|
||||
return specs
|
||||
|
||||
|
||||
def _display_specs(info: dict[str, object]) -> list[dict[str, object]]:
|
||||
probe = _PropertySpecProbe()
|
||||
probe.selected_kind = "face"
|
||||
specs = probe._property_editor_specs(info, info)
|
||||
return probe._sort_property_specs_for_display(specs)
|
||||
|
||||
|
||||
def _scope_mode(specs: list[dict[str, object]], key: str, mode: str) -> dict[str, object]:
|
||||
for spec in specs:
|
||||
if spec.get("key") != key:
|
||||
@@ -145,6 +152,33 @@ def _assert_no_generic_face_leak(keys: set[str], label: str) -> None:
|
||||
raise SystemExit(f"{label} leaked generic Face edit specs: {leaked}")
|
||||
|
||||
|
||||
def _is_actionable_edit_spec(spec: dict[str, object]) -> bool:
|
||||
return (
|
||||
bool(spec.get("editable"))
|
||||
and bool(spec.get("enabled"))
|
||||
and bool(spec.get("action"))
|
||||
and str(spec.get("value_type", "number")) != "command"
|
||||
)
|
||||
|
||||
|
||||
def _assert_actionable_rows_first(info: dict[str, object], expected_keys: tuple[str, ...], label: str) -> None:
|
||||
display_specs = _display_specs({**info, "readonly_probe_for_ordering": "readonly"})
|
||||
seen_non_actionable = False
|
||||
front_keys: list[str] = []
|
||||
for spec in display_specs:
|
||||
key = str(spec.get("key", ""))
|
||||
if _is_actionable_edit_spec(spec):
|
||||
if seen_non_actionable:
|
||||
raise SystemExit(f"{label}: actionable row {key!r} appeared after a read-only/non-action row")
|
||||
front_keys.append(key)
|
||||
else:
|
||||
seen_non_actionable = True
|
||||
|
||||
missing = [key for key in expected_keys if key not in front_keys]
|
||||
if missing:
|
||||
raise SystemExit(f"{label}: expected editable rows at the front are missing: {missing}; front={front_keys}")
|
||||
|
||||
|
||||
def _collect_legacy_face_terms(value: object, path: str = "specs") -> list[str]:
|
||||
legacy_terms = ("面内尺寸 1/2", "面内尺寸 1", "面内尺寸 2", "面位置", "偏移距离")
|
||||
hits: list[str] = []
|
||||
@@ -292,6 +326,14 @@ def main() -> int:
|
||||
"plane_origin": (0.0, 0.0, 0.0),
|
||||
"push_pull_outward_direction": (0.0, 0.0, 1.0),
|
||||
"normal": (0.0, 0.0, 1.0),
|
||||
"topology_relation_depth": 1,
|
||||
"topology_relation_status": "ready",
|
||||
"same_domain_face_count": 1,
|
||||
"first_level_boundary_edge_count": 4,
|
||||
"first_level_boundary_vertex_count": 4,
|
||||
"first_level_adjacent_face_count": 4,
|
||||
"first_level_topology_note": "已识别当前 Face 区域 1 个 Face、边界 Edge 4 条、边界 Vertex 4 个、共享边一级相邻 Face 4 个。",
|
||||
"topology_ignored_relation_note": "当前阶段只传播一级关系;二级、三级拓扑暂不自动递归编辑。",
|
||||
}
|
||||
plane_specs = _specs(plane_info)
|
||||
plane_keys = {str(spec.get("key", "")) for spec in plane_specs}
|
||||
@@ -309,7 +351,35 @@ def main() -> int:
|
||||
_assert_label(plane_specs, "local_face_width", "面宽")
|
||||
_assert_label(plane_specs, "local_face_height", "面高")
|
||||
_assert_label(plane_specs, "face_target_normal_position", "面偏移")
|
||||
_assert_actionable_rows_first(
|
||||
plane_info,
|
||||
(
|
||||
"area",
|
||||
"local_face_width",
|
||||
"local_face_height",
|
||||
"face_center_position",
|
||||
"face_target_normal_position",
|
||||
),
|
||||
"plane Face display order",
|
||||
)
|
||||
_assert_no_legacy_face_terms(plane_specs)
|
||||
plane_feature_probe = _PropertySpecProbe()
|
||||
plane_feature_probe.selected_kind = "feature"
|
||||
plane_feature_specs, _used = plane_feature_probe._editable_property_specs(plane_info)
|
||||
plane_feature_rows = plane_feature_probe._feature_property_specs(plane_feature_specs, plane_info)
|
||||
plane_feature_keys = {str(spec.get("key", "")) for spec in plane_feature_rows}
|
||||
topology_spec = _spec(plane_feature_rows, "face_first_level_topology")
|
||||
topology_text = str(topology_spec.get("current_text") or "")
|
||||
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}")
|
||||
if "face_target_normal_position" not in plane_feature_keys:
|
||||
raise SystemExit(
|
||||
"plane feature mode should expose the current Face offset parameter; "
|
||||
f"got {sorted(plane_feature_keys)}"
|
||||
)
|
||||
if "no_editable_feature_dimensions" in plane_feature_keys:
|
||||
raise SystemExit("plane feature mode should not fall back to no editable dimensions")
|
||||
_assert_hard_range(plane_specs, "area", 0.25, 2500.0)
|
||||
_assert_hard_range(plane_specs, "local_face_width", 0.5, 50.0)
|
||||
_assert_hard_range(plane_specs, "local_face_height", 0.5, 50.0)
|
||||
@@ -526,9 +596,18 @@ def main() -> int:
|
||||
),
|
||||
)
|
||||
for label, info, required in analytic_cases:
|
||||
keys = _spec_keys(info)
|
||||
specs = _specs(info)
|
||||
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_label(specs, "cone_reference_radius", "参考半径")
|
||||
_assert_label(specs, "cone_reference_diameter", "参考直径")
|
||||
_assert_label(specs, "cone_semi_angle_degrees", "圆锥半角")
|
||||
display_specs = _display_specs(info)
|
||||
surface_spec = _spec(display_specs, "surface")
|
||||
if surface_spec.get("current_text") != "圆锥面 / 拔模面":
|
||||
raise SystemExit(f"cone surface should be displayed in user-facing Chinese, got {surface_spec}")
|
||||
|
||||
_assert_target_change_detection()
|
||||
_assert_holed_plane_local_scopes_disabled()
|
||||
|
||||
Reference in New Issue
Block a user