feat: 增强一级关系识别与保持关系策略

This commit is contained in:
2026-08-10 14:56:50 +08:00
parent 12250603dd
commit 83c44265ba
24 changed files with 3460 additions and 241 deletions
+90
View File
@@ -58,6 +58,7 @@ def _strategy_label(strategy: str) -> str:
"auto": "自动选择",
"local-edge-only-deform": "只改当前Edge",
"move-edge-end-plane-by-push-pull": "移动端面/保持垂直",
"keep-first-level-planar-relations": "保持一级平面关系",
"resize-adjacent-cylinder-from-circular-edge-length": "相邻圆柱直径",
"scale-owning-shape-from-edge": "缩放所属",
}.get(strategy, strategy)
@@ -133,6 +134,7 @@ def _verify_line_edge_intent_specs() -> None:
_assert_edge_length_scope(length_spec, "auto", "自动", enabled=True)
_assert_edge_length_scope(length_spec, "local-edge-only-deform", "只改当前Edge", enabled=True)
_assert_edge_length_scope(length_spec, "move-edge-end-plane-by-push-pull", "移动端面", enabled=True)
_assert_edge_length_scope(length_spec, "keep-first-level-planar-relations", "保持关系", enabled=True)
_assert_edge_length_scope(
length_spec,
"resize-adjacent-cylinder-from-circular-edge-length",
@@ -140,6 +142,12 @@ def _verify_line_edge_intent_specs() -> None:
enabled=False,
)
_assert_edge_length_scope(length_spec, "scale-owning-shape-from-edge", "缩放所属", enabled=True)
constrained_specs = _specs_for_edge(info, strategy="keep-first-level-planar-relations", anchor="keep-start")
constrained_length_spec = _spec(constrained_specs, "length")
_assert(
str(constrained_length_spec.get("scope_default")) == "keep-first-level-planar-relations",
"line Edge should honor the explicit keep-planar-relations modeling intent",
)
anchor_spec = _spec(specs, "edge_length_anchor_mode")
_assert(str(anchor_spec.get("label")) == "长度基准", "line Edge should expose a length anchor row")
print("line Edge modeling-intent specs ok")
@@ -171,6 +179,7 @@ def _verify_circle_edge_intent_specs() -> None:
)
_assert_edge_length_scope(length_spec, "local-edge-only-deform", "只改当前Edge", enabled=False)
_assert_edge_length_scope(length_spec, "move-edge-end-plane-by-push-pull", "移动端面", enabled=False)
_assert_edge_length_scope(length_spec, "keep-first-level-planar-relations", "保持关系", enabled=False)
_assert_edge_length_scope(
length_spec,
"resize-adjacent-cylinder-from-circular-edge-length",
@@ -195,6 +204,7 @@ def _verify_edge_length_plan_intents() -> None:
expectations = (
("local-edge-only-deform", "keep-start", "local-edge-only-deform", "相邻面会自然变斜"),
("move-edge-end-plane-by-push-pull", "keep-start", "move-edge-end-plane-by-push-pull", "正方体这类模型会更像变成长方体"),
("keep-first-level-planar-relations", "keep-start", "move-edge-end-plane-by-push-pull", "保持一级平面关系"),
("scale-owning-shape-from-edge", "center", "scale-owning-shape-from-edge", "其它尺寸会跟随变化"),
)
for strategy, anchor, expected_strategy, impact_fragment in expectations:
@@ -206,6 +216,28 @@ def _verify_edge_length_plan_intents() -> None:
)
impact = str(plan.get("edge_length_impact_summary") or "")
_assert(impact_fragment in impact, f"{strategy} impact summary should explain result: {impact}")
if expected_strategy == "move-edge-end-plane-by-push-pull":
_assert(
plan.get("edge_length_planar_constraint_status") == "ready",
f"end-face push/pull should expose ready planar constraints: {plan}",
)
_assert(
"端面推拉平面约束" in str(plan.get("edge_length_planar_constraint_summary") or ""),
f"end-face push/pull should explain its planar constraint guard: {plan}",
)
_assert(
int(plan.get("edge_length_planar_constraint_perpendicular_count", 0) or 0) >= 1,
f"end-face push/pull should detect perpendicular side-face relations: {plan}",
)
if strategy == "keep-first-level-planar-relations":
_assert(
plan.get("edge_length_planar_relation_constraint_requested") is True,
f"explicit keep-planar-relations strategy should be preserved in the plan: {plan}",
)
_assert(
str(plan.get("edge_length_strategy_label")) == "保持一级平面关系",
f"explicit keep-planar-relations strategy should keep a user-facing label: {plan}",
)
blocked = model.general_edge_length_plan(
edge_id,
15.0,
@@ -214,13 +246,71 @@ def _verify_edge_length_plan_intents() -> None:
)
_assert(str(blocked.get("status")) == "blocked", "moving an end plane while fixing center should be blocked")
_assert("不能使用固定中心" in str(blocked.get("message") or ""), f"blocked message should explain center conflict: {blocked}")
blocked_keep_relations = model.general_edge_length_plan(
edge_id,
15.0,
anchor_mode="center",
strategy_mode="keep-first-level-planar-relations",
)
_assert(
str(blocked_keep_relations.get("status")) == "blocked",
"keeping first-level planar relations while fixing center should be blocked",
)
_assert(
"保持一级平面关系" in str(blocked_keep_relations.get("message") or ""),
f"blocked keep-planar-relations plan should name the requested constraint: {blocked_keep_relations}",
)
print("Edge modeling-intent plans ok")
def _verify_edge_coordinate_move_plan_semantics() -> None:
model = StepModel.load(DEFAULT_CUBE)
edge_id = _line_edge_ids_near_length(model, 10.0, 1e-5)[0]
info = model.edge_info(edge_id)
start = info.get("start_point")
center = info.get("length_center")
if not isinstance(start, (tuple, list)) or len(start) != 3:
raise AssertionError(f"line Edge lacks a stable start point: {info}")
if not isinstance(center, (tuple, list)) or len(center) != 3:
raise AssertionError(f"line Edge lacks a stable center: {info}")
endpoint_plan = model.edge_endpoint_move_plan(
edge_id,
"start",
(float(start[0]) - 1.0, float(start[1]), float(start[2])),
)
_assert(str(endpoint_plan.get("status")) != "blocked", f"endpoint move should be available: {endpoint_plan}")
_assert(str(endpoint_plan.get("edge_endpoint_label")) == "起点", f"endpoint label should be Chinese: {endpoint_plan}")
_assert(
str(endpoint_plan.get("edit_strategy_label")) == "Edge端点局部移动",
f"endpoint move should expose a user-facing strategy label: {endpoint_plan}",
)
_assert(
"不是端面整体推拉" in str(endpoint_plan.get("edit_semantics") or ""),
f"endpoint move should explain that it is local deformation: {endpoint_plan}",
)
center_plan = model.edge_center_move_plan(
edge_id,
(float(center[0]), float(center[1]), float(center[2]) + 1.0),
)
_assert(str(center_plan.get("status")) != "blocked", f"center move should be available: {center_plan}")
_assert(
str(center_plan.get("edit_strategy_label")) == "Edge整体局部移动",
f"center move should expose a user-facing strategy label: {center_plan}",
)
_assert(
"不是移动整个端面" in str(center_plan.get("edit_semantics") or ""),
f"center move should explain that it is local deformation: {center_plan}",
)
print("Edge coordinate move plan semantics ok")
def main() -> int:
_verify_line_edge_intent_specs()
_verify_circle_edge_intent_specs()
_verify_edge_length_plan_intents()
_verify_edge_coordinate_move_plan_semantics()
print("Edge modeling-intent spec suite passed.")
return 0