feat: 完善 Face 参数化编辑和隔离执行
This commit is contained in:
@@ -52,6 +52,75 @@ def _thickness_axis(size: tuple[float, float, float]) -> int:
|
||||
return min(range(3), key=lambda index: size[index])
|
||||
|
||||
|
||||
def _face_center(model: StepModel, face_id: int) -> tuple[float, float, float]:
|
||||
info = model.face_info(face_id)
|
||||
center = info.get("area_center") or info.get("bbox_center")
|
||||
if not isinstance(center, tuple) or len(center) != 3:
|
||||
raise SystemExit(f"Face {face_id} lacks a stable center")
|
||||
return float(center[0]), float(center[1]), float(center[2])
|
||||
|
||||
|
||||
def _face_area(model: StepModel, face_id: int) -> float:
|
||||
return float(model.face_info(face_id).get("area") or 0.0)
|
||||
|
||||
|
||||
def _distance(a: tuple[float, float, float], b: tuple[float, float, float]) -> float:
|
||||
return ((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2) ** 0.5
|
||||
|
||||
|
||||
def _vector(value: object, label: str) -> tuple[float, float, float]:
|
||||
if not isinstance(value, tuple) or len(value) != 3:
|
||||
raise SystemExit(f"{label} should be a 3D vector, got {value!r}")
|
||||
return float(value[0]), float(value[1]), float(value[2])
|
||||
|
||||
|
||||
def _axis_affine_point(
|
||||
point: tuple[float, float, float],
|
||||
axis_point: tuple[float, float, float],
|
||||
axis_direction: tuple[float, float, float],
|
||||
scale: float,
|
||||
) -> tuple[float, float, float]:
|
||||
length = (axis_direction[0] ** 2 + axis_direction[1] ** 2 + axis_direction[2] ** 2) ** 0.5
|
||||
if length <= 1e-12:
|
||||
raise SystemExit("owning shell thickness plan produced a zero axis direction")
|
||||
direction = (axis_direction[0] / length, axis_direction[1] / length, axis_direction[2] / length)
|
||||
relative = (point[0] - axis_point[0], point[1] - axis_point[1], point[2] - axis_point[2])
|
||||
along = relative[0] * direction[0] + relative[1] * direction[1] + relative[2] * direction[2]
|
||||
axial = (direction[0] * along, direction[1] * along, direction[2] * along)
|
||||
rest = (relative[0] - axial[0], relative[1] - axial[1], relative[2] - axial[2])
|
||||
return (
|
||||
axis_point[0] + rest[0] + axial[0] * scale,
|
||||
axis_point[1] + rest[1] + axial[1] * scale,
|
||||
axis_point[2] + rest[2] + axial[2] * scale,
|
||||
)
|
||||
|
||||
|
||||
def _assert_logical_face_retained(
|
||||
model: StepModel,
|
||||
logical_id: int,
|
||||
expected_center: tuple[float, float, float],
|
||||
expected_area: float,
|
||||
tolerance: float,
|
||||
label: str,
|
||||
) -> int:
|
||||
matches = model.face_ids_for_logical_id(logical_id)
|
||||
if not matches:
|
||||
raise SystemExit(f"{label} did not retain logical Face {logical_id}")
|
||||
resolved = model.resolve_face_selection_id(logical_id)
|
||||
if resolved is None or resolved not in matches:
|
||||
raise SystemExit(f"{label} logical Face {logical_id} did not resolve into matches {matches}")
|
||||
info = model.face_info(resolved)
|
||||
if info.get("surface") != "plane":
|
||||
raise SystemExit(f"{label} retained logical Face should be planar, got {info.get('surface')}")
|
||||
center = _face_center(model, resolved)
|
||||
if _distance(center, expected_center) > tolerance:
|
||||
raise SystemExit(f"{label} retained Face center should be {expected_center}, got {center}")
|
||||
area = _face_area(model, resolved)
|
||||
if abs(area - expected_area) > tolerance:
|
||||
raise SystemExit(f"{label} retained Face area should be {expected_area:g}, got {area:g}")
|
||||
return resolved
|
||||
|
||||
|
||||
def _verify_local_bounds(
|
||||
before_min: tuple[float, float, float],
|
||||
before_max: tuple[float, float, float],
|
||||
@@ -101,6 +170,9 @@ def _run_case(mode: str, source_thickness: float, target_thickness: float, toler
|
||||
_write_plate_model(model_path)
|
||||
model = StepModel.load(model_path)
|
||||
face_id = _first_shell_face(model, source_thickness, tolerance)
|
||||
logical_id = model.face_region_logical_id(face_id)
|
||||
before_face_center = _face_center(model, face_id)
|
||||
before_face_area = _face_area(model, face_id)
|
||||
before = model.stats()
|
||||
before_min, before_max, before_size = _bounds(model)
|
||||
axis = _thickness_axis(before_size)
|
||||
@@ -109,11 +181,24 @@ def _run_case(mode: str, source_thickness: float, target_thickness: float, toler
|
||||
plan = model.shell_thickness_plan(face_id, target_thickness)
|
||||
if plan["status"] == "blocked":
|
||||
raise SystemExit(f"local shell plan was blocked: {plan['message']}")
|
||||
outward = _vector(plan.get("outward_direction"), "local shell outward_direction")
|
||||
distance = float(plan.get("push_pull_distance", 0.0))
|
||||
expected_logical_center = (
|
||||
before_face_center[0] + outward[0] * distance,
|
||||
before_face_center[1] + outward[1] * distance,
|
||||
before_face_center[2] + outward[2] * distance,
|
||||
)
|
||||
result = model.resize_shell_thickness(face_id, target_thickness)
|
||||
elif mode == "owning":
|
||||
plan = model.shell_thickness_owning_scale_plan(face_id, target_thickness)
|
||||
if plan["status"] == "blocked":
|
||||
raise SystemExit(f"owning shell plan was blocked: {plan['message']}")
|
||||
expected_logical_center = _axis_affine_point(
|
||||
before_face_center,
|
||||
_vector(plan.get("affine_axis_point"), "owning shell affine_axis_point"),
|
||||
_vector(plan.get("affine_axis_direction"), "owning shell affine_axis_direction"),
|
||||
float(plan.get("affine_scale", 1.0)),
|
||||
)
|
||||
result = model.resize_shell_thickness_owning_scale(face_id, target_thickness)
|
||||
else:
|
||||
raise SystemExit(f"unsupported mode: {mode}")
|
||||
@@ -126,9 +211,19 @@ def _run_case(mode: str, source_thickness: float, target_thickness: float, toler
|
||||
_verify_local_bounds(before_min, before_max, after_min, after_max, axis, target_thickness, tolerance)
|
||||
else:
|
||||
_verify_owning_bounds(before_min, before_max, after_min, after_max, axis, target_thickness, tolerance)
|
||||
retained_face_id = _assert_logical_face_retained(
|
||||
model,
|
||||
logical_id,
|
||||
expected_logical_center,
|
||||
before_face_area,
|
||||
tolerance,
|
||||
f"{mode} shell thickness",
|
||||
)
|
||||
|
||||
print(f"mode={mode}")
|
||||
print(f"face_id={face_id}")
|
||||
print(f"logical_face_id={logical_id}")
|
||||
print(f"retained_logical_face={retained_face_id}")
|
||||
print(f"strategy={plan.get('resize_strategy')}")
|
||||
print(f"before={before}")
|
||||
print(f"after={after}")
|
||||
|
||||
Reference in New Issue
Block a user