diff --git a/tools/adaptive-isogrid/src/nx/extract_sandbox.py b/tools/adaptive-isogrid/src/nx/extract_sandbox.py index b4fd907f..710c05aa 100644 --- a/tools/adaptive-isogrid/src/nx/extract_sandbox.py +++ b/tools/adaptive-isogrid/src/nx/extract_sandbox.py @@ -169,41 +169,118 @@ def _sample_edge_polyline(edge: Any, chord_tol_mm: float, lister: Any = None) -> f"len={length:.3f} tol={tol:.3f} n_pts={n_pts}" ) - # 1) Primary: UF_MODL_ask_curve_props — normalized parameter 0..1 + # 1) Primary: UF curve evaluation — try multiple API patterns try: import NXOpen import NXOpen.UF uf = NXOpen.UF.UFSession.GetUFSession() - pts: List[Point3D] = [] - parse_failures = 0 + # Discover the correct method name (varies by NX version) + # UF_MODL_ask_curve_props → uf.Modl.AskCurveProps or similar + modl = uf.Modl + modl_methods = [m for m in dir(modl) if 'curve' in m.lower() or 'Curve' in m] + _log(f"[edge] UF.Modl curve methods: {modl_methods}") + + eval_obj = uf.Eval + eval_methods = [m for m in dir(eval_obj) if not m.startswith('_')] + _log(f"[edge] UF.Eval methods: {eval_methods}") + + # Also check uf.Curve try: - for i in range(n_pts + 1): - param = float(i) / float(n_pts) # 0.0 to 1.0 - # AskCurveProps returns: (point[3], tangent[3], normal[3], binormal[3], torsion, radius_of_curvature) - result = uf.Modl.AskCurveProps(edge.Tag, param) - # result[0] is the point array [x, y, z] - if isinstance(result, (list, tuple)) and len(result) >= 1: - pt_data = result[0] - if isinstance(pt_data, (list, tuple)) and len(pt_data) >= 3: - pts.append((float(pt_data[0]), float(pt_data[1]), float(pt_data[2]))) - elif hasattr(pt_data, '__len__') and len(pt_data) >= 3: - pts.append((float(pt_data[0]), float(pt_data[1]), float(pt_data[2]))) - else: - parse_failures += 1 - else: - parse_failures += 1 - finally: + curve_obj = uf.Curve + curve_methods = [m for m in dir(curve_obj) if 'eval' in m.lower() or 'Eval' in m or 'prop' in m.lower()] + _log(f"[edge] UF.Curve eval methods: {curve_methods}") + except Exception: pass + pts: List[Point3D] = [] + parse_failures = 0 + + # Try each possible API in order + eval_func = None + eval_style = None + + # Pattern A: uf.Modl.AskCurveProps(tag, param) — normalized 0..1 + for method_name in ('AskCurveProps', 'ask_curve_props'): + fn = getattr(modl, method_name, None) + if callable(fn): + eval_func = fn + eval_style = 'modl_props' + _log(f"[edge] Using uf.Modl.{method_name}") + break + + # Pattern B: uf.Eval.EvaluateUnitVectors or similar + if eval_func is None: + for method_name in eval_methods: + if 'valuat' in method_name.lower(): + eval_func = getattr(eval_obj, method_name) + eval_style = 'eval_' + method_name + _log(f"[edge] Using uf.Eval.{method_name}") + break + + # Pattern C: uf.Curve.EvaluateCurve(tag, param, deriv_flag) + if eval_func is None: + try: + curve_obj = uf.Curve + for method_name in ('EvaluateCurve', 'evaluate_curve'): + fn = getattr(curve_obj, method_name, None) + if callable(fn): + eval_func = fn + eval_style = 'curve_eval' + _log(f"[edge] Using uf.Curve.{method_name}") + break + except Exception: + pass + + if eval_func is None: + raise RuntimeError(f"No UF curve evaluation method found. Modl methods: {modl_methods}, Eval methods: {eval_methods}") + + try: + evaluator = None + if 'eval_' in (eval_style or ''): + evaluator = eval_obj.Initialize2(edge.Tag) + limits = eval_obj.AskLimits(evaluator) + t0, t1 = float(limits[0]), float(limits[1]) + + for i in range(n_pts + 1): + param = float(i) / float(n_pts) + + if eval_style == 'modl_props': + result = eval_func(edge.Tag, param) + elif eval_style == 'curve_eval': + # UF_CURVE_evaluate_curve(tag, natural_param, deriv_flag) → output array + # For arcs, natural param is in radians; for lines, arc length + # Use 0 = just point + t = t0 + (t1 - t0) * param if evaluator else param + result = eval_func(edge.Tag, t, 0) + else: + # eval_* method on evaluator + t = t0 + (t1 - t0) * param + result = eval_func(evaluator, 0, t) + + # Parse result — try multiple formats + pt = _parse_eval_point(result) + if pt is not None: + pts.append(pt) + else: + parse_failures += 1 + if parse_failures <= 2: + _log(f"[edge] Parse failed at param={param:.3f}, raw type={type(result).__name__}, repr={repr(result)[:200]}") + finally: + if evaluator is not None: + try: + eval_obj.Free(evaluator) + except Exception: + pass + if len(pts) >= 2: - _log(f"[edge] sampled via UF_MODL ({len(pts)} pts, {parse_failures} failures)") + _log(f"[edge] sampled via {eval_style} ({len(pts)} pts, {parse_failures} failures)") return pts - _log(f"[edge] UF_MODL insufficient points ({len(pts)}), falling back") + _log(f"[edge] {eval_style} insufficient points ({len(pts)}), falling back") except Exception as exc: - _log(f"[edge] UF_MODL failed: {exc}") + _log(f"[edge] UF curve eval failed: {exc}") # 2) Fallback: IBaseCurve.Evaluate (signature differs by NX versions) try: