From 851a8d3df0fa9b353bd4e76cf26a09bcb8183aaa Mon Sep 17 00:00:00 2001 From: Antoine Date: Mon, 16 Feb 2026 17:42:08 +0000 Subject: [PATCH] fix: replace face.GetLoops() with compatible API (GetEdgeLoops / UF layer / GetEdges fallback) --- .../src/nx/extract_sandbox.py | 109 ++++++++++++++---- 1 file changed, 89 insertions(+), 20 deletions(-) diff --git a/tools/adaptive-isogrid/src/nx/extract_sandbox.py b/tools/adaptive-isogrid/src/nx/extract_sandbox.py index 767dfa15..2891b8d0 100644 --- a/tools/adaptive-isogrid/src/nx/extract_sandbox.py +++ b/tools/adaptive-isogrid/src/nx/extract_sandbox.py @@ -136,12 +136,92 @@ def _close_polyline(points: List[Point3D]) -> List[Point3D]: # Face local frame # --------------------------------------------------------------------------- -def _face_local_frame(face: Any) -> LocalFrame: +def _get_face_loops(face: Any, lister: Any = None) -> List[Tuple[bool, List[Any]]]: + """ + Get edge loops from an NX face. + Returns list of (is_outer, [edges]) tuples. + + Tries multiple NX API patterns: + 1. face.GetEdgeLoops() (NX 2306+) + 2. UF layer: UF.Modeling.ask_face_loops() + 3. Fallback: all edges as single outer loop + """ + def _log(msg): + if lister: + lister.WriteLine(msg) + + # Method 1: GetEdgeLoops (modern NX) + try: + edge_loops = face.GetEdgeLoops() + if edge_loops: + result = [] + for i, el in enumerate(edge_loops): + edges = el.GetEdges() if hasattr(el, "GetEdges") else list(el) + is_outer = (i == 0) # first loop is typically outer + try: + is_outer = el.IsOuter() if hasattr(el, "IsOuter") else (i == 0) + except Exception: + pass + result.append((is_outer, list(edges))) + _log(f"[loops] GetEdgeLoops: {len(result)} loop(s)") + return result + except Exception: + pass + + # Method 2: UF layer + try: + import NXOpen + session = NXOpen.Session.GetSession() + uf = session.GetUFSession() + + face_tag = face.Tag + loop_count = [0] + loop_list = uf.Modeling.AskFaceLoops(face_tag) + # loop_list is (num_loops, loop_tags[]) + if loop_list and len(loop_list) >= 2: + num_loops = loop_list[0] + loop_tags = loop_list[1] + result = [] + for li in range(num_loops): + loop_tag = loop_tags[li] + # Get edges from loop + edge_info = uf.Modeling.AskLoopListOfEdges(loop_tag) + edges_tags = edge_info[1] if len(edge_info) >= 2 else [] + edges = [] + for et in edges_tags: + try: + edge_obj = NXOpen.TaggedObjectManager.GetTaggedObject(et) + edges.append(edge_obj) + except Exception: + pass + is_outer_val = uf.Modeling.AskLoopType(loop_tag) + is_outer = (is_outer_val == 1) # 1 = outer, 2 = inner typically + result.append((is_outer, edges)) + _log(f"[loops] UF layer: {len(result)} loop(s)") + return result + except Exception: + pass + + # Method 3: Fallback — all edges as single outer loop + try: + all_edges = face.GetEdges() + if all_edges: + edges = list(all_edges) + _log(f"[loops] Fallback: {len(edges)} edges as single outer loop") + return [(True, edges)] + except Exception: + pass + + return [] + + +def _face_local_frame(face: Any, lister: Any = None) -> LocalFrame: """ Build a stable local frame on a planar face. """ - loops = face.GetLoops() - first_edge = loops[0].GetEdges()[0] + # Get a sample point from the first edge + edges = face.GetEdges() + first_edge = edges[0] sample = _sample_edge_polyline(first_edge, chord_tol_mm=1.0)[0] # Get face normal @@ -387,17 +467,16 @@ def extract_sandbox_geometry( Extract a sandbox face into a JSON-serializable dict. Inner loops are boundary constraints (reserved geometry edges), not holes. """ - frame = _face_local_frame(face) + frame = _face_local_frame(face, lister) outer_2d: List[List[float]] = [] inner_boundaries: List[Dict[str, Any]] = [] - loops = face.GetLoops() + loops = _get_face_loops(face, lister) lister.WriteLine(f"[extract_sandbox] {sandbox_id}: {len(loops)} loop(s)") - for loop_index, loop in enumerate(loops): + for loop_index, (is_outer, edges) in enumerate(loops): loop_pts3d: List[Point3D] = [] - edges = loop.GetEdges() for edge in edges: pts = _sample_edge_polyline(edge, chord_tol_mm) if loop_pts3d and pts: @@ -407,13 +486,6 @@ def extract_sandbox_geometry( loop_pts3d = _close_polyline(loop_pts3d) loop_pts2d = project_to_2d(loop_pts3d, frame) - # Determine outer vs inner - is_outer = False - try: - is_outer = loop.IsOuter() - except Exception: - is_outer = (loop_index == 0) - if is_outer: outer_2d = [[round(x, 6), round(y, 6)] for x, y in loop_pts2d] lister.WriteLine(f"[extract_sandbox] outer loop: {len(outer_2d)} pts") @@ -494,13 +566,10 @@ def main(): # Debug: print face info lister.WriteLine(f"[extract_sandbox] Face type: {type(face).__name__}") try: - loops = face.GetLoops() - lister.WriteLine(f"[extract_sandbox] Loops: {len(loops)}") - for li, loop in enumerate(loops): - edges = loop.GetEdges() - lister.WriteLine(f"[extract_sandbox] Loop {li}: {len(edges)} edges") + all_edges = face.GetEdges() + lister.WriteLine(f"[extract_sandbox] Total edges on face: {len(all_edges)}") except Exception as exc: - lister.WriteLine(f"[extract_sandbox] GetLoops failed: {exc}") + lister.WriteLine(f"[extract_sandbox] GetEdges failed: {exc}") geom = extract_sandbox_geometry( face=face,