fix: handle closed circular edges (holes) - UF.Eval + GetLength circle fallback + debug logging
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user