diff --git a/tools/adaptive-isogrid/src/nx/extract_sandbox.py b/tools/adaptive-isogrid/src/nx/extract_sandbox.py index b5291106..29b30129 100644 --- a/tools/adaptive-isogrid/src/nx/extract_sandbox.py +++ b/tools/adaptive-isogrid/src/nx/extract_sandbox.py @@ -116,6 +116,42 @@ class EdgeSegment: # NX edge analysis — extract type + arc parameters # --------------------------------------------------------------------------- +def _parse_eval_result(result: Any) -> Point3D | None: + """Parse NX UF EvaluateUnitVectors return into a 3D point. + + Handles multiple NX Python return formats: + - NXOpen.Point3d with .X/.Y/.Z attrs + - Tuple/list of (point_obj, tangent_obj, ...) + - Nested numeric tuples ((x,y,z), (tx,ty,tz), ...) + - Flat numeric list [x,y,z,tx,ty,tz,...] + """ + # Direct Point3d-like object + if hasattr(result, 'X') and hasattr(result, 'Y') and hasattr(result, 'Z'): + return (float(result.X), float(result.Y), float(result.Z)) + + if isinstance(result, (list, tuple)): + # Check if first 3 elements are all numeric (flat array) + if len(result) >= 3 and all(isinstance(v, (int, float)) for v in result[:3]): + return (float(result[0]), float(result[1]), float(result[2])) + + # Check first element — could be a point object or sub-list + if len(result) >= 1: + first = result[0] + # Point3d-like object + if hasattr(first, 'X') and hasattr(first, 'Y') and hasattr(first, 'Z'): + return (float(first.X), float(first.Y), float(first.Z)) + # Sub-list/tuple of 3 numbers + if isinstance(first, (list, tuple)) and len(first) >= 3: + if all(isinstance(v, (int, float)) for v in first[:3]): + return (float(first[0]), float(first[1]), float(first[2])) + # Recurse into first element + parsed = _parse_eval_result(first) + if parsed is not None: + return parsed + + return None + + def _analyze_edge(edge: Any, lister: Any = None) -> EdgeSegment: """ Analyze an NX edge and return a typed EdgeSegment. @@ -137,13 +173,18 @@ def _analyze_edge(edge: Any, lister: Any = None) -> EdgeSegment: # Classify edge type edge_type_str = "?" + edge_type_val = None try: - edge_type_str = str(edge.SolidEdgeType) + edge_type_val = edge.SolidEdgeType + edge_type_str = str(edge_type_val) except Exception: pass - is_linear = "Linear" in edge_type_str - is_circular = "Circular" in edge_type_str + # NX returns either enum name ("Linear") or integer (1=linear, 2=circular, etc.) + is_linear = ("Linear" in edge_type_str or edge_type_val == 1 + or edge_type_str == "1") + is_circular = ("Circular" in edge_type_str or edge_type_val == 2 + or edge_type_str == "2") # Linear edges — simple if is_linear: @@ -198,15 +239,12 @@ def _analyze_edge(edge: Any, lister: Any = None) -> EdgeSegment: limits = eval_obj.AskLimits(evaluator) t_mid = (float(limits[0]) + float(limits[1])) / 2.0 result = eval_obj.EvaluateUnitVectors(evaluator, t_mid) - # Parse point from result - if hasattr(result, '__len__') and len(result) >= 1: - item = result[0] if hasattr(result[0], 'X') else result - if hasattr(item, 'X'): - mid_pt = (float(item.X), float(item.Y), float(item.Z)) - elif isinstance(item, (list, tuple)) and len(item) >= 3: - mid_pt = (float(item[0]), float(item[1]), float(item[2])) - if mid_pt is None and hasattr(result, 'X'): - mid_pt = (float(result.X), float(result.Y), float(result.Z)) + # Parse point from result — multiple possible formats: + # 1) NXOpen.Point3d with .X/.Y/.Z + # 2) Tuple of (point, tangent, ...) where point has .X/.Y/.Z + # 3) Nested lists/tuples like ((x,y,z), (tx,ty,tz), ...) + # 4) Flat list [x,y,z,tx,ty,tz,...] + mid_pt = _parse_eval_result(result) except Exception as exc2: _log(f"[edge] Arc midpoint sampling failed: {exc2}")