fix(extract): discover UF curve eval methods dynamically

NXOpen Python wraps UF methods with version-specific names.
Now dumps available methods on UF.Modl, UF.Eval, UF.Curve
and tries them in order. Detailed logging shows which method
was found and used, plus raw result format on parse failures.
This commit is contained in:
2026-02-17 01:33:39 +00:00
parent fc1c1dc142
commit fbdafb9a37

View File

@@ -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}" 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: try:
import NXOpen import NXOpen
import NXOpen.UF import NXOpen.UF
uf = NXOpen.UF.UFSession.GetUFSession() uf = NXOpen.UF.UFSession.GetUFSession()
pts: List[Point3D] = [] # Discover the correct method name (varies by NX version)
parse_failures = 0 # 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: try:
for i in range(n_pts + 1): curve_obj = uf.Curve
param = float(i) / float(n_pts) # 0.0 to 1.0 curve_methods = [m for m in dir(curve_obj) if 'eval' in m.lower() or 'Eval' in m or 'prop' in m.lower()]
# AskCurveProps returns: (point[3], tangent[3], normal[3], binormal[3], torsion, radius_of_curvature) _log(f"[edge] UF.Curve eval methods: {curve_methods}")
result = uf.Modl.AskCurveProps(edge.Tag, param) except Exception:
# 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:
pass 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: 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 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: 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) # 2) Fallback: IBaseCurve.Evaluate (signature differs by NX versions)
try: try: