feat: 完善 STEP/B-Rep 一级关系参数化编辑
This commit is contained in:
@@ -46,6 +46,15 @@ def _edge_specs(info: dict[str, object]) -> list[dict[str, object]]:
|
||||
return specs
|
||||
|
||||
|
||||
def _edge_display_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 = probe._property_editor_specs(info, info)
|
||||
return probe._sort_property_specs_for_display(specs)
|
||||
|
||||
|
||||
def _display_specs(info: dict[str, object]) -> list[dict[str, object]]:
|
||||
probe = _PropertySpecProbe()
|
||||
probe.selected_kind = "face"
|
||||
@@ -105,7 +114,7 @@ def _assert_current_text_contains(
|
||||
def _assert_spans_value_columns(specs: list[dict[str, object]], key: str) -> None:
|
||||
spec = _spec(specs, key)
|
||||
if not bool(spec.get("span_value_columns")):
|
||||
raise SystemExit(f"{key} should span the value/intent/target/action columns: {spec}")
|
||||
raise SystemExit(f"{key} should span the value/intent/target columns: {spec}")
|
||||
|
||||
|
||||
def _assert_hard_range(specs: list[dict[str, object]], key: str, low: float, high: float) -> None:
|
||||
@@ -195,8 +204,6 @@ 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:
|
||||
@@ -210,6 +217,13 @@ def _assert_actionable_rows_first(info: dict[str, object], expected_keys: tuple[
|
||||
raise SystemExit(f"{label}: expected editable rows at the front are missing: {missing}; front={front_keys}")
|
||||
|
||||
|
||||
def _assert_keys_absent(specs: list[dict[str, object]], forbidden_keys: tuple[str, ...], label: str) -> None:
|
||||
keys = {str(spec.get("key", "")) for spec in specs}
|
||||
leaked = [key for key in forbidden_keys if key in keys]
|
||||
if leaked:
|
||||
raise SystemExit(f"{label} should not show diagnostic/non-editable rows in the parameter table: {leaked}")
|
||||
|
||||
|
||||
def _collect_legacy_face_terms(value: object, path: str = "specs") -> list[str]:
|
||||
legacy_terms = (
|
||||
"面内尺寸 1/2",
|
||||
@@ -296,7 +310,7 @@ def _assert_target_change_detection() -> None:
|
||||
probe = _PropertySpecProbe()
|
||||
|
||||
number_spec = {
|
||||
"label": "面积",
|
||||
"label": "面内长度",
|
||||
"current_raw": 100.0,
|
||||
"current_text": "100",
|
||||
"value_type": "positive",
|
||||
@@ -321,19 +335,17 @@ def _assert_target_change_detection() -> None:
|
||||
def _assert_property_table_column_widths() -> None:
|
||||
for width in (320, 340, 360, 400, 520):
|
||||
columns = _property_table_column_widths(width)
|
||||
if len(columns) != 5:
|
||||
raise SystemExit(f"property table should have five column widths, got {columns}")
|
||||
if len(columns) != 4:
|
||||
raise SystemExit(f"property table should have four column widths, got {columns}")
|
||||
if sum(columns) != width:
|
||||
raise SystemExit(f"property table widths should fill viewport {width}, got {columns} sum={sum(columns)}")
|
||||
label_width, current_width, scope_width, target_width, action_width = columns
|
||||
if current_width < label_width + 40:
|
||||
raise SystemExit(f"current value column should be wider than parameter-name column at {width}: {columns}")
|
||||
if action_width < 70:
|
||||
raise SystemExit(f"operation column should keep row buttons visible at {width}: {columns}")
|
||||
label_width, current_width, scope_width, target_width = columns
|
||||
if current_width < 96:
|
||||
raise SystemExit(f"current value column should stay readable at {width}: {columns}")
|
||||
if target_width < 56:
|
||||
raise SystemExit(f"target value column should stay usable at {width}: {columns}")
|
||||
if scope_width > label_width + 8:
|
||||
raise SystemExit(f"modeling-intent column should not take space from current values at {width}: {columns}")
|
||||
if scope_width < 44:
|
||||
raise SystemExit(f"modeling-intent column should stay usable at {width}: {columns}")
|
||||
|
||||
|
||||
def _assert_holed_plane_local_scopes_disabled() -> None:
|
||||
@@ -355,7 +367,7 @@ def _assert_holed_plane_local_scopes_disabled() -> None:
|
||||
"local_face_deform_blocker": "has inner boundary",
|
||||
}
|
||||
)
|
||||
for key in ("area", "local_face_width", "local_face_height", "face_center_position"):
|
||||
for key in ("local_face_width", "local_face_height", "face_center_position"):
|
||||
local_mode = _scope_mode(specs, key, "local")
|
||||
if bool(local_mode.get("enabled", True)):
|
||||
raise SystemExit(f"{key} local Face scope should be disabled for a holed planar Face")
|
||||
@@ -429,26 +441,44 @@ def main() -> int:
|
||||
_assert_current_text_contains(
|
||||
plane_specs,
|
||||
"cad_modeling_form",
|
||||
("柔性建模", "拉伸切除", "偏移变换"),
|
||||
("柔性建模", "拉伸切除", "偏移"),
|
||||
"plane Face",
|
||||
)
|
||||
_assert_hint_fragments(
|
||||
_spec(plane_specs, "cad_modeling_form").get("disabled_tip"),
|
||||
("CAD 语义判断", "这东西像什么建模对象"),
|
||||
"CAD modeling form tooltip",
|
||||
)
|
||||
_assert_label(plane_specs, "cad_recommended_operation", "推荐操作")
|
||||
_assert_spans_value_columns(plane_specs, "cad_recommended_operation")
|
||||
_assert_current_text_contains(
|
||||
plane_specs,
|
||||
"cad_recommended_operation",
|
||||
("优先改偏移变换", "拉伸/切除"),
|
||||
("优先改偏移", "拉伸/切除"),
|
||||
"plane Face",
|
||||
)
|
||||
_assert_hint_fragments(
|
||||
_spec(plane_specs, "cad_recommended_operation").get("disabled_tip"),
|
||||
("相对安全的改法建议", "建议你怎么改"),
|
||||
"recommended operation tooltip",
|
||||
)
|
||||
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]}")
|
||||
if any(str(spec.get("key", "")) == "area" for spec in plane_display_specs):
|
||||
raise SystemExit("Face property table should keep area in diagnostics, not in the parameter table")
|
||||
_assert_keys_absent(
|
||||
plane_display_specs,
|
||||
(
|
||||
"cad_modeling_form",
|
||||
"cad_recommended_operation",
|
||||
"face_first_level_topology",
|
||||
"face_edit_semantics",
|
||||
"feature_context_note",
|
||||
),
|
||||
"plane Face display specs",
|
||||
)
|
||||
_assert_contains(
|
||||
plane_keys,
|
||||
{
|
||||
"area",
|
||||
"local_face_width",
|
||||
"local_face_height",
|
||||
"face_center_position",
|
||||
@@ -456,13 +486,14 @@ def main() -> int:
|
||||
},
|
||||
"plane Face",
|
||||
)
|
||||
_assert_label(plane_specs, "local_face_width", "U向尺寸")
|
||||
_assert_label(plane_specs, "local_face_height", "V向尺寸")
|
||||
_assert_label(plane_specs, "face_target_normal_position", "偏移变换")
|
||||
if "area" in plane_keys:
|
||||
raise SystemExit("plane Face should not expose area as a modifiable parameter")
|
||||
_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",
|
||||
@@ -476,22 +507,18 @@ def main() -> int:
|
||||
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")
|
||||
_assert_spans_value_columns(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}")
|
||||
_assert_label(plane_feature_rows, "face_edit_semantics", "建模意图")
|
||||
_assert_spans_value_columns(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]}")
|
||||
_assert_keys_absent(
|
||||
plane_feature_rows,
|
||||
(
|
||||
"cad_modeling_form",
|
||||
"cad_recommended_operation",
|
||||
"face_first_level_topology",
|
||||
"face_edit_semantics",
|
||||
"feature_context_note",
|
||||
),
|
||||
"plane feature mode display specs",
|
||||
)
|
||||
expected_plane_feature_keys = {
|
||||
"area",
|
||||
"local_face_width",
|
||||
"local_face_height",
|
||||
"face_center_position",
|
||||
@@ -506,7 +533,6 @@ def main() -> int:
|
||||
)
|
||||
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)
|
||||
_assert_hard_range(plane_specs, "face_target_normal_position", -70.7106781185, 70.7106781185)
|
||||
@@ -514,8 +540,6 @@ def main() -> int:
|
||||
_assert_validation_error(probe, _spec(plane_specs, "local_face_width"), "0.49", True, "Face width below hard range")
|
||||
_assert_validation_error(probe, _spec(plane_specs, "local_face_width"), "0.5", False, "Face width lower boundary")
|
||||
_assert_validation_error(probe, _spec(plane_specs, "local_face_width"), "50.1", True, "Face width above hard range")
|
||||
_assert_validation_error(probe, _spec(plane_specs, "area"), "0.2", True, "Face area below hard range")
|
||||
_assert_validation_error(probe, _spec(plane_specs, "area"), "2500", False, "Face area upper boundary")
|
||||
_assert_validation_error(
|
||||
probe,
|
||||
_spec(plane_specs, "face_target_normal_position"),
|
||||
@@ -543,7 +567,7 @@ def main() -> int:
|
||||
)
|
||||
if "push_pull_distance" in plane_keys:
|
||||
raise SystemExit("plane Face should not expose a separate push_pull_distance row")
|
||||
for key in ("area", "local_face_width", "local_face_height"):
|
||||
for key in ("local_face_width", "local_face_height"):
|
||||
for mode in ("local", "owning"):
|
||||
_assert_scoped_hint_fragments(
|
||||
plane_specs,
|
||||
@@ -664,7 +688,18 @@ def main() -> int:
|
||||
cylinder_feature_probe = _PropertySpecProbe()
|
||||
cylinder_feature_specs, _used = cylinder_feature_probe._editable_property_specs(cylinder_topology_info)
|
||||
cylinder_feature_rows = cylinder_feature_probe._feature_property_specs(cylinder_feature_specs, cylinder_topology_info)
|
||||
_spec(cylinder_feature_rows, "cylindrical_feature_first_level_topology")
|
||||
_assert_keys_absent(
|
||||
cylinder_feature_rows,
|
||||
(
|
||||
"cad_modeling_form",
|
||||
"cad_recommended_operation",
|
||||
"cylindrical_feature_first_level_topology",
|
||||
"hole_edit_semantics",
|
||||
"slot_edit_semantics",
|
||||
"feature_context_note",
|
||||
),
|
||||
"cylindrical feature mode display specs",
|
||||
)
|
||||
|
||||
generic_cylinder_specs = _specs(
|
||||
{
|
||||
@@ -820,6 +855,8 @@ def main() -> int:
|
||||
"radius": 1.0,
|
||||
"angular_span": 1.5707963267948966,
|
||||
"existing_fillet_radius": 1.0,
|
||||
"existing_fillet_arc_length_estimate": 1.5707963267948966,
|
||||
"existing_fillet_angular_span": 1.5707963267948966,
|
||||
"feature_existing_fillet_support_face_ids": (1, 2),
|
||||
"area": 3.0,
|
||||
"area_center": (0.0, 0.0, 0.0),
|
||||
@@ -830,7 +867,11 @@ def main() -> int:
|
||||
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_contains(
|
||||
fillet_keys,
|
||||
{"existing_fillet_radius_estimate", "existing_fillet_arc_length_estimate"},
|
||||
"existing fillet feature",
|
||||
)
|
||||
_assert_current_text_contains(
|
||||
fillet_specs,
|
||||
"cad_modeling_form",
|
||||
@@ -914,9 +955,7 @@ def main() -> int:
|
||||
_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_keys_absent(display_specs, ("surface",), "cone feature display specs")
|
||||
|
||||
edge_specs = _edge_specs(
|
||||
{
|
||||
@@ -963,6 +1002,39 @@ def main() -> int:
|
||||
{"length", "edge_first_level_topology"},
|
||||
"line Edge",
|
||||
)
|
||||
_assert_contains(
|
||||
{str(spec.get("key", "")) for spec in _edge_display_specs({"curve": "line", "length": 10.0})},
|
||||
{"length", "edge_length_anchor_mode"},
|
||||
"line Edge display",
|
||||
)
|
||||
|
||||
complex_edge_specs = _edge_specs({"curve": "b-spline curve", "length": 12.0})
|
||||
complex_length_spec = _spec(complex_edge_specs, "length")
|
||||
if complex_length_spec.get("enabled"):
|
||||
raise SystemExit(f"complex curve Edge length should not be enabled in the property specs: {complex_length_spec}")
|
||||
complex_length_tip = str(complex_length_spec.get("disabled_tip") or "")
|
||||
if "复杂曲线Edge暂未实现" not in complex_length_tip:
|
||||
raise SystemExit(f"complex curve Edge length tip should explain unsupported editing: {complex_length_tip}")
|
||||
complex_edge_keys = {str(spec.get("key", "")) for spec in _edge_display_specs({"curve": "b-spline curve", "length": 12.0})}
|
||||
if "length" in complex_edge_keys or "edge_length_anchor_mode" in complex_edge_keys:
|
||||
raise SystemExit(f"complex curve Edge display should hide length editing rows: {complex_edge_keys}")
|
||||
|
||||
ellipse_display_specs = _edge_display_specs(
|
||||
{
|
||||
"curve": "ellipse",
|
||||
"length": 23.0,
|
||||
"major_radius": 5.0,
|
||||
"minor_radius": 2.0,
|
||||
}
|
||||
)
|
||||
ellipse_display_keys = {str(spec.get("key", "")) for spec in ellipse_display_specs}
|
||||
_assert_contains(
|
||||
ellipse_display_keys,
|
||||
{"ellipse_edge_major_radius", "ellipse_edge_minor_radius"},
|
||||
"ellipse Edge display",
|
||||
)
|
||||
if "length" in ellipse_display_keys or "edge_length_anchor_mode" in ellipse_display_keys:
|
||||
raise SystemExit(f"ellipse Edge display should prefer explicit axis radii over generic length: {ellipse_display_keys}")
|
||||
|
||||
low_recognition_specs = _specs(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user