Files
Atomizer/tools/adaptive-isogrid/src/nx/import_profile.py

164 lines
5.6 KiB
Python
Raw Normal View History

"""
NXOpen script import rib profile JSON and replace sandbox geometry.
Input:
rib_profile_<sandbox_id>.json (or rib_profile.json)
Responsibilities:
- Recreate closed polylines from profile coordinate arrays
- Build sheet region for sandbox
- Replace sandbox face geometry only
- Sew/unite with neighboring reserved faces
"""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from typing import Any, Dict, Iterable, List, Sequence, Tuple
Point2D = Tuple[float, float]
Point3D = Tuple[float, float, float]
def _add(a: Sequence[float], b: Sequence[float]) -> Point3D:
return (a[0] + b[0], a[1] + b[1], a[2] + b[2])
def _mul(v: Sequence[float], s: float) -> Point3D:
return (v[0] * s, v[1] * s, v[2] * s)
def load_json(path: Path) -> Dict[str, Any]:
return json.loads(path.read_text())
def map_2d_to_3d(p: Point2D, transform: Dict[str, List[float]]) -> Point3D:
origin = transform["origin"]
x_axis = transform["x_axis"]
y_axis = transform["y_axis"]
return _add(_add(origin, _mul(x_axis, p[0])), _mul(y_axis, p[1]))
def _ensure_closed(coords: List[Point2D]) -> List[Point2D]:
if not coords:
return coords
if coords[0] != coords[-1]:
coords.append(coords[0])
return coords
def _create_polyline_curve(work_part: Any, pts3d: List[Point3D]) -> Any:
"""
Create a closed polyline curve in NX.
API notes: this can be implemented with StudioSplineBuilderEx, PolygonBuilder,
or line segments + composite curve depending on NX version/license.
"""
# Line-segment fallback (works in all NX versions)
curves = []
for i in range(len(pts3d) - 1):
p1 = work_part.Points.CreatePoint(pts3d[i])
p2 = work_part.Points.CreatePoint(pts3d[i + 1])
curves.append(work_part.Curves.CreateLine(p1, p2))
return curves
def build_profile_curves(work_part: Any, profile: Dict[str, Any], transform: Dict[str, List[float]]) -> Dict[str, List[Any]]:
created: Dict[str, List[Any]] = {"outer": [], "pockets": [], "holes": []}
outer = _ensure_closed([(float(x), float(y)) for x, y in profile["outer_boundary"]])
outer_3d = [map_2d_to_3d(p, transform) for p in outer]
created["outer"] = _create_polyline_curve(work_part, outer_3d)
for pocket in profile.get("pockets", []):
coords = _ensure_closed([(float(x), float(y)) for x, y in pocket])
pts3d = [map_2d_to_3d(p, transform) for p in coords]
created["pockets"].extend(_create_polyline_curve(work_part, pts3d))
for hole in profile.get("hole_boundaries", []):
coords = _ensure_closed([(float(x), float(y)) for x, y in hole])
pts3d = [map_2d_to_3d(p, transform) for p in coords]
created["holes"].extend(_create_polyline_curve(work_part, pts3d))
return created
def _find_sandbox_face(work_part: Any, sandbox_id: str) -> Any:
for body in getattr(work_part.Bodies, "ToArray", lambda: work_part.Bodies)():
for face in body.GetFaces():
try:
tag = face.GetStringUserAttribute("ISOGRID_SANDBOX", -1)
except Exception:
tag = None
if tag == sandbox_id:
return face
raise RuntimeError(f"Sandbox face not found for id={sandbox_id}")
def replace_sandbox_face_geometry(work_part: Any, sandbox_face: Any, created_curves: Dict[str, List[Any]]) -> None:
"""
Replace sandbox surface region from generated profile curves.
This operation depends on the model topology and NX license package.
Typical implementation:
1) Build bounded plane/sheet from outer and inner loops
2) Trim/split host face by new boundaries
3) Delete old sandbox patch
4) Sew new patch with reserved neighboring faces
5) Unite if multiple sheet bodies are produced
"""
# Recommended implementation hook points.
# - Through Curve Mesh / Bounded Plane builders in NXOpen.Features
# - SewBuilder in NXOpen.Features
# - DeleteFace + ReplaceFace in synchronous modeling toolkit
raise NotImplementedError(
"Sandbox face replacement is model-specific. Implement with NXOpen feature builders "
"(bounded sheet + replace face + sew/unite) in target NX environment."
)
def run_in_nx(
profile_path: Path,
geometry_path: Path,
sandbox_id: str,
) -> None:
import NXOpen # type: ignore
session = NXOpen.Session.GetSession()
work_part = session.Parts.Work
if work_part is None:
raise RuntimeError("No active NX work part.")
profile = load_json(profile_path)
geometry = load_json(geometry_path)
transform = geometry.get("transform")
if not transform:
raise ValueError(f"Missing transform in {geometry_path}")
sandbox_face = _find_sandbox_face(work_part, sandbox_id)
created_curves = build_profile_curves(work_part, profile, transform)
replace_sandbox_face_geometry(work_part, sandbox_face, created_curves)
print(f"[import_profile] Imported profile for {sandbox_id}: {profile_path}")
def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Import rib profile JSON into NX sandbox face")
parser.add_argument("--profile", required=True, help="Path to rib_profile json")
parser.add_argument("--geometry", required=True, help="Path to geometry_sandbox json")
parser.add_argument("--sandbox-id", required=True, help="Sandbox id (e.g. sandbox_1)")
args = parser.parse_args(argv)
run_in_nx(
profile_path=Path(args.profile),
geometry_path=Path(args.geometry),
sandbox_id=args.sandbox_id,
)
return 0
if __name__ == "__main__":
raise SystemExit(main())