feat: 完善参数化编辑语义和槽孔中心距
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from step_editor.model import StepModel
|
||||
|
||||
DEFAULT_MODEL = PROJECT_ROOT / "assets" / "models" / "cube_10mm.step"
|
||||
|
||||
|
||||
def _first_plane_face_near_size(model: StepModel, width: float, height: float, tolerance: float) -> int:
|
||||
for face_id in range(len(model.faces)):
|
||||
info = model.face_info(face_id)
|
||||
if info.get("surface") != "plane":
|
||||
continue
|
||||
face_width = float(info.get("local_face_width") or 0.0)
|
||||
face_height = float(info.get("local_face_height") or 0.0)
|
||||
direct = abs(face_width - width) <= tolerance and abs(face_height - height) <= tolerance
|
||||
swapped = abs(face_width - height) <= tolerance and abs(face_height - width) <= tolerance
|
||||
if direct or swapped:
|
||||
return face_id
|
||||
raise SystemExit(f"no plane Face near {width:g} x {height:g}")
|
||||
|
||||
|
||||
def _plane_sizes(model: StepModel) -> list[tuple[float, float, float]]:
|
||||
sizes: list[tuple[float, float, float]] = []
|
||||
for face_id in range(len(model.faces)):
|
||||
info = model.face_info(face_id)
|
||||
if info.get("surface") != "plane":
|
||||
continue
|
||||
area = float(info.get("area") or 0.0)
|
||||
width = float(info.get("local_face_width") or 0.0)
|
||||
height = float(info.get("local_face_height") or 0.0)
|
||||
sizes.append((round(area, 6), round(width, 6), round(height, 6)))
|
||||
return sorted(sizes)
|
||||
|
||||
|
||||
def _has_plane_size(model: StepModel, width: float, height: float, tolerance: float) -> bool:
|
||||
for _area, face_width, face_height in _plane_sizes(model):
|
||||
direct = abs(face_width - width) <= tolerance and abs(face_height - height) <= tolerance
|
||||
swapped = abs(face_width - height) <= tolerance and abs(face_height - width) <= tolerance
|
||||
if direct or swapped:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Verify Face resize semantics on the cube test model.")
|
||||
parser.add_argument("model", nargs="?", default=str(DEFAULT_MODEL), help="STEP model path.")
|
||||
parser.add_argument("--axis", default="width", choices=["width", "height"])
|
||||
parser.add_argument("--source-size", type=float, default=10.0)
|
||||
parser.add_argument("--other-size", type=float, default=10.0)
|
||||
parser.add_argument("--target-size", type=float, default=15.0)
|
||||
parser.add_argument("--strategy", default="local", choices=["local", "owning"])
|
||||
parser.add_argument("--tolerance", type=float, default=1e-5)
|
||||
args = parser.parse_args()
|
||||
|
||||
model = StepModel.load(Path(args.model))
|
||||
face_id = _first_plane_face_near_size(model, args.source_size, args.other_size, args.tolerance)
|
||||
before = model.stats()
|
||||
|
||||
if args.strategy == "local":
|
||||
plan = model.face_size_local_resize_plan(face_id, args.target_size, args.axis)
|
||||
result = model.resize_face_size_local(face_id, args.target_size, args.axis)
|
||||
expected_strategy = f"local-face-{args.axis}-only-deform"
|
||||
else:
|
||||
plan = model.face_size_owning_scale_plan(face_id, args.target_size, args.axis)
|
||||
result = model.resize_face_size_owning_scale(face_id, args.target_size, args.axis)
|
||||
expected_strategy = f"axis-scale-owning-shape-from-face-{args.axis}"
|
||||
|
||||
resolved_strategy = str(plan.get("resize_strategy", ""))
|
||||
if resolved_strategy != expected_strategy:
|
||||
raise SystemExit(f"expected {expected_strategy}, got {resolved_strategy or '<none>'}")
|
||||
|
||||
after = model.stats()
|
||||
if after.solids != before.solids:
|
||||
raise SystemExit(f"solid count changed: before={before.solids}, after={after.solids}")
|
||||
if after.faces < before.faces:
|
||||
raise SystemExit(f"face count decreased: before={before.faces}, after={after.faces}")
|
||||
|
||||
width = args.target_size if args.axis == "width" else args.other_size
|
||||
height = args.other_size if args.axis == "width" else args.target_size
|
||||
if not _has_plane_size(model, width, height, args.tolerance):
|
||||
raise SystemExit(f"no resulting plane Face near {width:g} x {height:g}")
|
||||
|
||||
print(f"model={Path(args.model)}")
|
||||
print(f"face_id={face_id}")
|
||||
print(f"strategy={args.strategy}")
|
||||
print(f"axis={args.axis}")
|
||||
print(f"resolved_strategy={resolved_strategy}")
|
||||
print(f"rebuild_mode={plan.get('owning_face_size_rebuild_mode', '')}")
|
||||
print(f"before={before}")
|
||||
print(f"after={after}")
|
||||
print(f"plane_sizes={_plane_sizes(model)}")
|
||||
print(result.encode("ascii", "backslashreplace").decode("ascii"))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user