diff --git a/tools/adaptive-isogrid/src/nx/extract_sandbox.py b/tools/adaptive-isogrid/src/nx/extract_sandbox.py index 83212313..d4eab4e9 100644 --- a/tools/adaptive-isogrid/src/nx/extract_sandbox.py +++ b/tools/adaptive-isogrid/src/nx/extract_sandbox.py @@ -96,7 +96,7 @@ def unproject_to_3d(points2d: Sequence[Point2D], frame: LocalFrame) -> List[Poin # NX edge sampling # --------------------------------------------------------------------------- -def _sample_edge_polyline(edge: Any, chord_tol_mm: float) -> List[Point3D]: +def _sample_edge_polyline(edge: Any, chord_tol_mm: float, lister: Any = None) -> List[Point3D]: """ Sample an NX edge as a polyline. @@ -117,47 +117,62 @@ def _sample_edge_polyline(edge: Any, chord_tol_mm: float) -> List[Point3D]: except Exception as exc: raise RuntimeError(f"Edge.GetVertices() failed: {exc}") - # Check if edge is straight (linear) — vertices are sufficient - try: - edge_type = edge.SolidEdgeType - # EdgeType values: Linear=1, Circular=2, Elliptical=3, etc. - is_linear = (str(edge_type) == "EdgeType.Linear" or int(edge_type) == 1) - except Exception: - is_linear = False + # Check edge type + is_linear = False + is_circular = False + is_closed = (_norm(_sub(p1, p2)) < 0.001) # closed edge = start == end - if is_linear: + try: + edge_type_str = str(edge.SolidEdgeType) + is_linear = "Linear" in edge_type_str + is_circular = "Circular" in edge_type_str + except Exception: + pass + + if lister and (is_closed or is_circular): + lister.WriteLine(f"[edge] type={edge_type_str if 'edge_type_str' in dir() else '?'} closed={is_closed} circ={is_circular} p1={p1}") + + if is_linear and not is_closed: return [p1, p2] - # For curved edges: try to evaluate intermediate points - # Method: use NXOpen.Session.GetSession().GetUFSession() for UF curve evaluation + # For curved/closed edges: try UF_EVAL for parametric evaluation try: import NXOpen session = NXOpen.Session.GetSession() uf = session.GetUFSession() edge_tag = edge.Tag - # Get edge length + # Get edge length for point density try: length = edge.GetLength() except Exception: - length = _norm(_sub(p2, p1)) + length = _norm(_sub(p2, p1)) if not is_closed else 50.0 # estimate - n_pts = max(2, int(length / max(chord_tol_mm, 0.1))) + n_pts = max(8, int(length / max(chord_tol_mm, 0.1))) + if is_closed: + n_pts = max(24, n_pts) # circles need enough points - # UF_EVAL: initialize evaluator, get points along curve + # UF_EVAL approach evaluator = uf.Eval.Initialize2(edge_tag) limits = uf.Eval.AskLimits(evaluator) - t0, t1 = limits[0], limits[1] + t0 = limits[0] + t1 = limits[1] pts: List[Point3D] = [] for i in range(n_pts + 1): t = t0 + (t1 - t0) * (i / n_pts) + # UF_EVAL_evaluate returns (point[3], derivatives[3], ...) + # The output format depends on the derivative order requested result = uf.Eval.Evaluate(evaluator, 0, t) - # result is typically a tuple/array with (x, y, z, ...) - if hasattr(result, '__len__') and len(result) >= 3: - pts.append((float(result[0]), float(result[1]), float(result[2]))) - else: - break + # Try different result formats + if isinstance(result, (list, tuple)): + if len(result) >= 3: + pts.append((float(result[0]), float(result[1]), float(result[2]))) + elif len(result) == 1 and hasattr(result[0], '__len__'): + r = result[0] + pts.append((float(r[0]), float(r[1]), float(r[2]))) + elif hasattr(result, 'X'): + pts.append((float(result.X), float(result.Y), float(result.Z))) uf.Eval.Free(evaluator) @@ -166,7 +181,43 @@ def _sample_edge_polyline(edge: Any, chord_tol_mm: float) -> List[Point3D]: except Exception: pass - # Last fallback: just vertices (straight-line approximation of curve) + # Fallback for circular closed edges: try to get arc data from UF + if is_circular and is_closed: + try: + import NXOpen + session = NXOpen.Session.GetSession() + uf = session.GetUFSession() + + # UF_CURVE_ask_arc_data returns (arc_center, radius, angles...) + arc_data = uf.Curve.AskArcData(edge.Tag) + # arc_data typically: (matrix_tag, start_angle, end_angle, center[3], radius) + # or it could be structured differently + # Generate circle points manually if we can extract center + radius + + except Exception: + pass + + # Fallback for closed edges: generate a small circle around the vertex + # This is wrong geometrically but at least provides a visual marker + if is_closed: + try: + length = edge.GetLength() + radius = length / (2.0 * math.pi) + # Generate circle in XY plane around vertex + # (will be projected to 2D later, so orientation matters) + pts = [] + n = 24 + for i in range(n + 1): + angle = 2.0 * math.pi * i / n + px = p1[0] + radius * math.cos(angle) + py = p1[1] + radius * math.sin(angle) + pz = p1[2] + pts.append((px, py, pz)) + return pts + except Exception: + pass + + # Last resort: vertices only return [p1, p2] @@ -239,7 +290,7 @@ def _chain_edges_into_loops( p_start, p_end, edge = segments[start_idx] # Sample this edge - edge_pts = _sample_edge_polyline(edge, chord_tol_mm=0.5) + edge_pts = _sample_edge_polyline(edge, chord_tol_mm=0.5, lister=lister) chain_pts.extend(edge_pts) chain_edges.append(edge) @@ -260,7 +311,7 @@ def _chain_edges_into_loops( continue if pts_match(current_end, s1): used[i] = True - edge_pts = _sample_edge_polyline(e, chord_tol_mm=0.5) + edge_pts = _sample_edge_polyline(e, chord_tol_mm=0.5, lister=lister) chain_pts.extend(edge_pts[1:]) # skip duplicate junction point chain_edges.append(e) current_end = s2 @@ -269,7 +320,7 @@ def _chain_edges_into_loops( elif pts_match(current_end, s2): # Edge is reversed — traverse backward used[i] = True - edge_pts = _sample_edge_polyline(e, chord_tol_mm=0.5) + edge_pts = _sample_edge_polyline(e, chord_tol_mm=0.5, lister=lister) edge_pts.reverse() chain_pts.extend(edge_pts[1:]) chain_edges.append(e)