refactor(brain): structured pocket output — 3 lines + 3 arcs per pocket
Replaced Shapely buffer-based fillet (59-pt polylines) with exact geometric fillet computation. Each pocket now outputs: - 3 straight edges (line start/end pairs) - 3 fillet arcs (center, radius, tangent points, angles) NX import updated to use SketchLineBuilder + SketchArcBuilder (3-point). Total NX entities: ~2,600 (was ~13,000). Includes arc fallback to 2-line segments if SketchArcBuilder fails. Also outputs circular hole definitions for future NX circle creation.
This commit is contained in:
@@ -4,7 +4,7 @@ NXOpen script — Import rib profile into NX as a sketch.
|
||||
Reads `rib_profile_<sandbox_id>.json` (output from Python Brain) and creates
|
||||
an NX sketch on the sandbox plane containing:
|
||||
- Outer boundary polyline
|
||||
- All pocket cutout polylines
|
||||
- Structured pocket geometry (3 lines + 3 arcs per triangular pocket)
|
||||
|
||||
The sketch is placed in the idealized part. Antoine extrudes manually the first
|
||||
time; subsequent iterations only update the sketch and the extrude regenerates.
|
||||
@@ -55,6 +55,22 @@ def unproject_to_3d(
|
||||
return pts3d
|
||||
|
||||
|
||||
def unproject_point_to_3d(
|
||||
pt2d: List[float],
|
||||
transform: Dict[str, List[float]],
|
||||
) -> Tuple[float, float, float]:
|
||||
"""Convert a single 2D point to 3D."""
|
||||
ox, oy, oz = transform["origin"]
|
||||
xx, xy, xz = transform["x_axis"]
|
||||
yx, yy, yz = transform["y_axis"]
|
||||
x, y = pt2d[0], pt2d[1]
|
||||
return (
|
||||
ox + x * xx + y * yx,
|
||||
oy + x * xy + y * yy,
|
||||
oz + x * xz + y * yz,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# NX sketch creation
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -82,7 +98,6 @@ def _find_or_create_sketch(
|
||||
except Exception:
|
||||
continue
|
||||
if fname == sketch_name:
|
||||
# Get the sketch from the feature
|
||||
try:
|
||||
existing_sketch = feat.GetEntities()[0]
|
||||
lister.WriteLine(f"[import] Found existing sketch: {sketch_name}")
|
||||
@@ -93,7 +108,6 @@ def _find_or_create_sketch(
|
||||
pass
|
||||
|
||||
if existing_sketch is not None:
|
||||
# Clear existing geometry for update
|
||||
try:
|
||||
existing_sketch.Activate(False)
|
||||
all_geom = existing_sketch.GetAllGeometry()
|
||||
@@ -116,31 +130,20 @@ def _find_or_create_sketch(
|
||||
normal_vec = NXOpen.Vector3d(normal[0], normal[1], normal[2])
|
||||
x_vec = NXOpen.Vector3d(x_axis[0], x_axis[1], x_axis[2])
|
||||
|
||||
# Create a Plane (SmartObject) — NOT a DatumPlane (DisplayableObject)
|
||||
# PlaneCollection.CreatePlane(method, alternate, origin, normal, expr, flip, percent, geometry)
|
||||
# Create a Plane (SmartObject)
|
||||
plane = part.Planes.CreatePlane(
|
||||
NXOpen.PlaneTypes.MethodType.Fixed, # Fixed plane
|
||||
NXOpen.PlaneTypes.MethodType.Fixed,
|
||||
NXOpen.PlaneTypes.AlternateType.One,
|
||||
origin_pt,
|
||||
normal_vec,
|
||||
"", # expression
|
||||
False, # flip
|
||||
False, # percent
|
||||
[], # geometry refs
|
||||
"",
|
||||
False,
|
||||
False,
|
||||
[],
|
||||
)
|
||||
lister.WriteLine(f"[import] Created plane: {plane} (type={type(plane).__name__})")
|
||||
|
||||
if plane is None:
|
||||
# Fallback: try Distance method
|
||||
plane = part.Planes.CreatePlane(
|
||||
NXOpen.PlaneTypes.MethodType.Distance,
|
||||
NXOpen.PlaneTypes.AlternateType.One,
|
||||
origin_pt, normal_vec, "", False, False, [],
|
||||
)
|
||||
lister.WriteLine(f"[import] Fallback plane: {plane}")
|
||||
|
||||
if plane is None:
|
||||
# Fallback 2: CreateFixedTypePlane
|
||||
y_axis_t = transform["y_axis"]
|
||||
mtx = NXOpen.Matrix3x3()
|
||||
mtx.Xx = x_axis[0]; mtx.Xy = x_axis[1]; mtx.Xz = x_axis[2]
|
||||
@@ -149,94 +152,50 @@ def _find_or_create_sketch(
|
||||
plane = part.Planes.CreateFixedTypePlane(
|
||||
origin_pt, mtx, NXOpen.SmartObject.UpdateOption.WithinModeling,
|
||||
)
|
||||
lister.WriteLine(f"[import] Fallback2 plane: {plane}")
|
||||
lister.WriteLine(f"[import] Fallback plane: {plane}")
|
||||
|
||||
# Create sketch-in-place builder
|
||||
# Build sketch
|
||||
sketch_builder = part.Sketches.CreateSketchInPlaceBuilder2(NXOpen.Sketch.Null)
|
||||
|
||||
# NXOpen Python naming collision: property getter and setter method share
|
||||
# the same name. builder.PlaneReference returns None (getter), so calling
|
||||
# builder.PlaneReference(plane) = None(plane) = TypeError.
|
||||
# Solution: use setattr() to bypass the getter and hit the setter.
|
||||
origin_point = part.Points.CreatePoint(origin_pt)
|
||||
axis_dir = part.Directions.CreateDirection(
|
||||
origin_pt, x_vec, NXOpen.SmartObject.UpdateOption.WithinModeling,
|
||||
)
|
||||
|
||||
# Try multiple setter patterns
|
||||
# Use setattr to bypass property/method naming collision
|
||||
for attr, val, label in [
|
||||
("PlaneReference", plane, "plane"),
|
||||
("SketchOrigin", origin_point, "origin"),
|
||||
("AxisReference", axis_dir, "axis"),
|
||||
]:
|
||||
# Pattern 1: setattr (property assignment)
|
||||
try:
|
||||
setattr(sketch_builder, attr, val)
|
||||
lister.WriteLine(f"[import] Set {label} via setattr")
|
||||
continue
|
||||
except Exception as e1:
|
||||
pass
|
||||
|
||||
# Pattern 2: find the setter method with Set prefix
|
||||
setter_name = "Set" + attr
|
||||
try:
|
||||
setter = getattr(sketch_builder, setter_name, None)
|
||||
if setter:
|
||||
setter(val)
|
||||
lister.WriteLine(f"[import] Set {label} via {setter_name}()")
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
lister.WriteLine(f"[import] WARNING: Could not set {label}")
|
||||
|
||||
# Pattern 3: direct call (in case NX version supports it)
|
||||
try:
|
||||
getattr(sketch_builder, attr)(val)
|
||||
lister.WriteLine(f"[import] Set {label} via method call")
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
lister.WriteLine(f"[import] WARNING: Could not set {label} ({attr})")
|
||||
|
||||
# Commit — Builder.Commit() returns NXObject
|
||||
sketch_obj = sketch_builder.Commit()
|
||||
lister.WriteLine(f"[import] Commit returned: {sketch_obj} (type={type(sketch_obj).__name__})")
|
||||
|
||||
# Also get committed objects for inspection
|
||||
committed = sketch_builder.GetCommittedObjects()
|
||||
lister.WriteLine(f"[import] Committed objects: {len(committed)}")
|
||||
for i, obj in enumerate(committed):
|
||||
lister.WriteLine(f"[import] [{i}] {type(obj).__name__}: {obj}")
|
||||
|
||||
sketch_builder.Destroy()
|
||||
|
||||
# Find the Sketch object from committed objects or return value
|
||||
# Get Sketch object
|
||||
sketch = None
|
||||
if isinstance(sketch_obj, NXOpen.Sketch):
|
||||
sketch = sketch_obj
|
||||
else:
|
||||
# Search committed objects
|
||||
for obj in committed:
|
||||
if isinstance(obj, NXOpen.Sketch):
|
||||
sketch = obj
|
||||
break
|
||||
# Try GetObject
|
||||
if sketch is None:
|
||||
try:
|
||||
sketch = sketch_builder.GetObject()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Last resort: find by name in part sketches
|
||||
if sketch is None:
|
||||
for s in part.Sketches:
|
||||
sketch = s # take the last one (just created)
|
||||
lister.WriteLine(f"[import] Found sketch by iteration: {sketch}")
|
||||
|
||||
sketch = s
|
||||
if sketch is None:
|
||||
raise RuntimeError("Could not get Sketch object after commit")
|
||||
|
||||
# Rename
|
||||
try:
|
||||
sketch.Name = sketch_name
|
||||
except Exception:
|
||||
@@ -246,90 +205,133 @@ def _find_or_create_sketch(
|
||||
return sketch
|
||||
|
||||
|
||||
def _draw_polylines_batch(
|
||||
part: Any,
|
||||
sketch: Any,
|
||||
polylines_3d: List[List[Tuple[float, float, float]]],
|
||||
lister: Any,
|
||||
close: bool = True,
|
||||
) -> int:
|
||||
"""
|
||||
Draw multiple closed polylines directly in the active sketch using
|
||||
SketchLineBuilder (creates native sketch geometry, not model curves).
|
||||
|
||||
The sketch must be Activate'd before calling this.
|
||||
|
||||
Returns total number of line segments created.
|
||||
"""
|
||||
import NXOpen
|
||||
|
||||
total_lines = 0
|
||||
|
||||
for points_3d in polylines_3d:
|
||||
if len(points_3d) < 2:
|
||||
def _resolve_enum(paths):
|
||||
"""Try multiple dotted paths to resolve an NXOpen enum value."""
|
||||
for path in paths:
|
||||
try:
|
||||
parts_p = path.split(".")
|
||||
obj = __import__(parts_p[0])
|
||||
for attr in parts_p[1:]:
|
||||
obj = getattr(obj, attr)
|
||||
return obj, path
|
||||
except (AttributeError, ImportError):
|
||||
continue
|
||||
|
||||
n = len(points_3d)
|
||||
# Strip closing duplicate
|
||||
if close and n >= 3:
|
||||
d = math.sqrt(sum((a - b) ** 2 for a, b in zip(points_3d[0], points_3d[-1])))
|
||||
if d < 0.001:
|
||||
n = n - 1
|
||||
|
||||
segments = n if close else (n - 1)
|
||||
|
||||
for i in range(segments):
|
||||
p1 = points_3d[i]
|
||||
p2 = points_3d[(i + 1) % n]
|
||||
|
||||
try:
|
||||
lb = part.Sketches.CreateLineBuilder()
|
||||
lb.SetStartPoint(NXOpen.Point3d(p1[0], p1[1], p1[2]))
|
||||
lb.SetEndPoint(NXOpen.Point3d(p2[0], p2[1], p2[2]))
|
||||
lb.Commit()
|
||||
lb.Destroy()
|
||||
total_lines += 1
|
||||
except Exception as exc:
|
||||
if total_lines == 0:
|
||||
lister.WriteLine(f"[import] SketchLineBuilder failed on first line: {exc}")
|
||||
return 0
|
||||
|
||||
# Progress every 50 polylines
|
||||
if (total_lines > 0) and (len(polylines_3d) > 50) and \
|
||||
(polylines_3d.index(points_3d) % 50 == 49):
|
||||
lister.WriteLine(f"[import] ... {total_lines} lines so far")
|
||||
|
||||
return total_lines
|
||||
return None, None
|
||||
|
||||
|
||||
def _draw_circle_in_sketch(
|
||||
part: Any,
|
||||
sketch: Any,
|
||||
center_3d: Tuple[float, float, float],
|
||||
radius: float,
|
||||
normal: List[float],
|
||||
lister: Any,
|
||||
) -> bool:
|
||||
"""Draw a circle in the sketch."""
|
||||
# ---------------------------------------------------------------------------
|
||||
# Sketch drawing: lines and arcs
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _draw_line(part, p1_3d, p2_3d):
|
||||
"""Draw a single line in the active sketch. Returns True on success."""
|
||||
import NXOpen
|
||||
lb = part.Sketches.CreateLineBuilder()
|
||||
lb.SetStartPoint(NXOpen.Point3d(p1_3d[0], p1_3d[1], p1_3d[2]))
|
||||
lb.SetEndPoint(NXOpen.Point3d(p2_3d[0], p2_3d[1], p2_3d[2]))
|
||||
lb.Commit()
|
||||
lb.Destroy()
|
||||
return True
|
||||
|
||||
try:
|
||||
circle_builder = part.Sketches.CreateCircleBuilder()
|
||||
center_pt = NXOpen.Point3d(center_3d[0], center_3d[1], center_3d[2])
|
||||
circle_builder.SetCenterPoint(center_pt)
|
||||
# Size point = center + radius along x
|
||||
size_pt = NXOpen.Point3d(
|
||||
center_3d[0] + radius,
|
||||
center_3d[1],
|
||||
center_3d[2],
|
||||
)
|
||||
circle_builder.SetSizePoint(size_pt)
|
||||
circle_builder.Commit()
|
||||
circle_builder.Destroy()
|
||||
return True
|
||||
except Exception as exc:
|
||||
lister.WriteLine(f"[import] Circle creation failed: {exc}")
|
||||
return False
|
||||
|
||||
def _draw_arc_3pt(part, start_3d, mid_3d, end_3d):
|
||||
"""
|
||||
Draw a 3-point arc in the active sketch.
|
||||
SketchArcBuilder: SetStartPoint, SetEndPoint, SetThirdPoint, Commit.
|
||||
"""
|
||||
import NXOpen
|
||||
ab = part.Sketches.CreateArcBuilder()
|
||||
ab.SetStartPoint(NXOpen.Point3d(start_3d[0], start_3d[1], start_3d[2]))
|
||||
ab.SetEndPoint(NXOpen.Point3d(end_3d[0], end_3d[1], end_3d[2]))
|
||||
ab.SetThirdPoint(NXOpen.Point3d(mid_3d[0], mid_3d[1], mid_3d[2]))
|
||||
ab.Commit()
|
||||
ab.Destroy()
|
||||
return True
|
||||
|
||||
|
||||
def _arc_midpoint_2d(arc):
|
||||
"""Compute the midpoint of a fillet arc in 2D (for 3-point arc creation)."""
|
||||
cx, cy = arc['center']
|
||||
r = arc['radius']
|
||||
sa = arc['start_angle']
|
||||
ea = arc['end_angle']
|
||||
# Go the short way around
|
||||
da = ea - sa
|
||||
if da > math.pi:
|
||||
da -= 2 * math.pi
|
||||
elif da < -math.pi:
|
||||
da += 2 * math.pi
|
||||
mid_angle = sa + da / 2.0
|
||||
return [cx + r * math.cos(mid_angle), cy + r * math.sin(mid_angle)]
|
||||
|
||||
|
||||
def _draw_outer_boundary(part, outer_2d, transform, lister):
|
||||
"""Draw the outer boundary as a closed polyline."""
|
||||
outer_3d = unproject_to_3d(outer_2d, transform)
|
||||
n = len(outer_3d)
|
||||
if n < 2:
|
||||
return 0
|
||||
|
||||
# Strip closing duplicate
|
||||
if n >= 3:
|
||||
d = math.sqrt(sum((a - b) ** 2 for a, b in zip(outer_3d[0], outer_3d[-1])))
|
||||
if d < 0.001:
|
||||
n -= 1
|
||||
|
||||
count = 0
|
||||
for i in range(n):
|
||||
p1 = outer_3d[i]
|
||||
p2 = outer_3d[(i + 1) % n]
|
||||
try:
|
||||
_draw_line(part, p1, p2)
|
||||
count += 1
|
||||
except Exception as exc:
|
||||
if count == 0:
|
||||
lister.WriteLine(f"[import] ERROR: First line failed: {exc}")
|
||||
return 0
|
||||
return count
|
||||
|
||||
|
||||
def _draw_structured_pocket(part, pocket, transform, lister):
|
||||
"""
|
||||
Draw a structured pocket (lines + arcs) in the active sketch.
|
||||
Returns (num_lines, num_arcs) created.
|
||||
"""
|
||||
lines_drawn = 0
|
||||
arcs_drawn = 0
|
||||
|
||||
# Draw straight edges
|
||||
for line in pocket.get('lines', []):
|
||||
start_3d = unproject_point_to_3d(line[0], transform)
|
||||
end_3d = unproject_point_to_3d(line[1], transform)
|
||||
try:
|
||||
_draw_line(part, start_3d, end_3d)
|
||||
lines_drawn += 1
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Draw fillet arcs (3-point: start, midpoint, end)
|
||||
for arc in pocket.get('arcs', []):
|
||||
start_3d = unproject_point_to_3d(arc['tangent_start'], transform)
|
||||
end_3d = unproject_point_to_3d(arc['tangent_end'], transform)
|
||||
mid_2d = _arc_midpoint_2d(arc)
|
||||
mid_3d = unproject_point_to_3d(mid_2d, transform)
|
||||
try:
|
||||
_draw_arc_3pt(part, start_3d, mid_3d, end_3d)
|
||||
arcs_drawn += 1
|
||||
except Exception as exc:
|
||||
if arcs_drawn == 0:
|
||||
lister.WriteLine(f"[import] Arc creation failed: {exc}")
|
||||
# Fallback: draw as 2 line segments through midpoint
|
||||
try:
|
||||
_draw_line(part, start_3d, mid_3d)
|
||||
_draw_line(part, mid_3d, end_3d)
|
||||
lines_drawn += 2
|
||||
lister.WriteLine("[import] Arc fallback: 2 lines")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return lines_drawn, arcs_drawn
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -352,7 +354,6 @@ def main():
|
||||
part_name = work_part.Name if hasattr(work_part, "Name") else ""
|
||||
lister.WriteLine(f"[import] Work part: {part_name}")
|
||||
|
||||
# If not in idealized part, find it
|
||||
if not part_name.endswith("_i"):
|
||||
for part in session.Parts:
|
||||
pname = part.Name if hasattr(part, "Name") else ""
|
||||
@@ -362,7 +363,6 @@ def main():
|
||||
lister.WriteLine(f"[import] Switched to idealized part: {pname}")
|
||||
break
|
||||
|
||||
# Find data directory
|
||||
try:
|
||||
part_dir = os.path.dirname(work_part.FullPath)
|
||||
except Exception:
|
||||
@@ -374,22 +374,18 @@ def main():
|
||||
lister.WriteLine(f"[import] ERROR: Data directory not found: {data_dir}")
|
||||
return
|
||||
|
||||
# Find all rib profile + geometry JSON pairs
|
||||
# Find rib profile JSONs
|
||||
profile_files = sorted([
|
||||
f for f in os.listdir(data_dir)
|
||||
if f.startswith("rib_profile_") and f.endswith(".json")
|
||||
])
|
||||
|
||||
if not profile_files:
|
||||
# Also check for sandbox1_rib_profile.json pattern
|
||||
profile_files = sorted([
|
||||
f for f in os.listdir(data_dir)
|
||||
if "rib_profile" in f and f.endswith(".json")
|
||||
])
|
||||
|
||||
if not profile_files:
|
||||
lister.WriteLine(f"[import] ERROR: No rib_profile*.json found in {data_dir}")
|
||||
lister.WriteLine(f"[import] Files present: {os.listdir(data_dir)}")
|
||||
lister.WriteLine(f"[import] ERROR: No rib_profile*.json in {data_dir}")
|
||||
return
|
||||
|
||||
lister.WriteLine(f"[import] Found {len(profile_files)} profile(s) to import")
|
||||
@@ -397,21 +393,17 @@ def main():
|
||||
for profile_file in profile_files:
|
||||
profile_path = os.path.join(data_dir, profile_file)
|
||||
|
||||
# Determine sandbox_id from filename
|
||||
# Expected: rib_profile_sandbox_1.json or sandbox1_rib_profile.json
|
||||
sandbox_id = profile_file.replace("rib_profile_", "").replace(".json", "")
|
||||
if not sandbox_id.startswith("sandbox"):
|
||||
sandbox_id = "sandbox_1" # fallback
|
||||
sandbox_id = "sandbox_1"
|
||||
|
||||
# Load corresponding geometry JSON for the transform
|
||||
geom_path = os.path.join(data_dir, f"geometry_{sandbox_id}.json")
|
||||
if not os.path.exists(geom_path):
|
||||
# Try alternate names
|
||||
candidates = [f for f in os.listdir(data_dir) if f.startswith("geometry_") and f.endswith(".json")]
|
||||
if candidates:
|
||||
geom_path = os.path.join(data_dir, candidates[0])
|
||||
else:
|
||||
lister.WriteLine(f"[import] ERROR: No geometry JSON found for transform data")
|
||||
lister.WriteLine("[import] ERROR: No geometry JSON for transform")
|
||||
continue
|
||||
|
||||
lister.WriteLine(f"\n--- Importing {profile_file} ---")
|
||||
@@ -429,7 +421,7 @@ def main():
|
||||
transform = geometry["transform"]
|
||||
sketch_name = SKETCH_NAME_PREFIX + sandbox_id
|
||||
|
||||
# Find or create sketch
|
||||
# Create sketch
|
||||
try:
|
||||
sketch = _find_or_create_sketch(work_part, sketch_name, transform, lister)
|
||||
except Exception as exc:
|
||||
@@ -438,86 +430,81 @@ def main():
|
||||
lister.WriteLine(traceback.format_exc())
|
||||
continue
|
||||
|
||||
# Activate sketch for drawing
|
||||
# Activate sketch
|
||||
try:
|
||||
# ViewReorient enum — try multiple access paths
|
||||
view_false = None
|
||||
for path in [
|
||||
"NXOpen.ViewReorient.FalseValue",
|
||||
view_false, path = _resolve_enum([
|
||||
"NXOpen.Sketch.ViewReorient.FalseValue",
|
||||
"NXOpen.SketchViewReorient.FalseValue",
|
||||
]:
|
||||
try:
|
||||
parts_p = path.split(".")
|
||||
obj = __import__(parts_p[0])
|
||||
for attr in parts_p[1:]:
|
||||
obj = getattr(obj, attr)
|
||||
view_false = obj
|
||||
lister.WriteLine(f"[import] ViewReorient resolved via: {path}")
|
||||
break
|
||||
except (AttributeError, ImportError):
|
||||
continue
|
||||
|
||||
"NXOpen.ViewReorient.FalseValue",
|
||||
])
|
||||
if view_false is None:
|
||||
# Last resort: pass False as boolean
|
||||
view_false = False
|
||||
lister.WriteLine("[import] ViewReorient: using False (boolean fallback)")
|
||||
|
||||
lister.WriteLine(f"[import] ViewReorient: {path or 'boolean fallback'}")
|
||||
sketch.Activate(view_false)
|
||||
except Exception as exc:
|
||||
lister.WriteLine(f"[import] ERROR activating sketch: {exc}")
|
||||
continue
|
||||
|
||||
total_lines = 0
|
||||
|
||||
# Collect all polylines to draw
|
||||
all_polylines_3d = []
|
||||
|
||||
# Outer boundary
|
||||
outer_2d = profile.get("outer_boundary", [])
|
||||
if outer_2d:
|
||||
outer_3d = unproject_to_3d(outer_2d, transform)
|
||||
all_polylines_3d.append(outer_3d)
|
||||
|
||||
# Pocket cutouts
|
||||
# --- Draw geometry ---
|
||||
pockets = profile.get("pockets", [])
|
||||
lister.WriteLine(f"[import] Preparing {len(pockets)} pockets + outer boundary...")
|
||||
outer_2d = profile.get("outer_boundary", [])
|
||||
|
||||
for pocket_pts in pockets:
|
||||
if len(pocket_pts) < 3:
|
||||
continue
|
||||
pocket_3d = unproject_to_3d(pocket_pts, transform)
|
||||
all_polylines_3d.append(pocket_3d)
|
||||
# Check if pockets are structured (lines+arcs) or legacy (point lists)
|
||||
is_structured = (len(pockets) > 0 and isinstance(pockets[0], dict)
|
||||
and 'lines' in pockets[0])
|
||||
|
||||
# Draw everything in one batch
|
||||
lister.WriteLine(f"[import] Creating {len(all_polylines_3d)} polylines as NX curves...")
|
||||
total_lines = _draw_polylines_batch(
|
||||
work_part, sketch, all_polylines_3d, lister, close=True,
|
||||
)
|
||||
if is_structured:
|
||||
lister.WriteLine(f"[import] Structured format: {len(pockets)} pockets + outer boundary")
|
||||
|
||||
# Outer boundary
|
||||
outer_lines = _draw_outer_boundary(work_part, outer_2d, transform, lister)
|
||||
lister.WriteLine(f"[import] Outer boundary: {outer_lines} lines ({len(outer_2d)} pts)")
|
||||
|
||||
# Pockets
|
||||
total_lines = outer_lines
|
||||
total_arcs = 0
|
||||
for idx, pocket in enumerate(pockets):
|
||||
nl, na = _draw_structured_pocket(work_part, pocket, transform, lister)
|
||||
total_lines += nl
|
||||
total_arcs += na
|
||||
if (idx + 1) % 50 == 0:
|
||||
lister.WriteLine(f"[import] ... {idx+1}/{len(pockets)} pockets drawn")
|
||||
|
||||
lister.WriteLine(f"[import] Done: {total_lines} lines + {total_arcs} arcs in sketch")
|
||||
lister.WriteLine(f"[import] Expected: ~{len(pockets)*3} lines + ~{len(pockets)*3} arcs")
|
||||
else:
|
||||
# Legacy format: pockets are point lists
|
||||
lister.WriteLine(f"[import] Legacy format: {len(pockets)} pocket polylines")
|
||||
outer_lines = _draw_outer_boundary(work_part, outer_2d, transform, lister)
|
||||
total_lines = outer_lines
|
||||
for pocket_pts in pockets:
|
||||
if len(pocket_pts) < 3:
|
||||
continue
|
||||
pocket_3d = unproject_to_3d(pocket_pts, transform)
|
||||
n = len(pocket_3d)
|
||||
d = math.sqrt(sum((a - b)**2 for a, b in zip(pocket_3d[0], pocket_3d[-1])))
|
||||
if d < 0.001:
|
||||
n -= 1
|
||||
for i in range(n):
|
||||
try:
|
||||
_draw_line(work_part, pocket_3d[i], pocket_3d[(i+1) % n])
|
||||
total_lines += 1
|
||||
except Exception:
|
||||
pass
|
||||
lister.WriteLine(f"[import] Done: {total_lines} lines in sketch")
|
||||
|
||||
# Deactivate sketch
|
||||
try:
|
||||
# Resolve UpdateLevel enum at runtime
|
||||
update_model = None
|
||||
for path in ["NXOpen.Sketch.UpdateLevel.Model", "NXOpen.UpdateLevel.Model"]:
|
||||
try:
|
||||
parts_p = path.split(".")
|
||||
obj = __import__(parts_p[0])
|
||||
for attr in parts_p[1:]:
|
||||
obj = getattr(obj, attr)
|
||||
update_model = obj
|
||||
break
|
||||
except (AttributeError, ImportError):
|
||||
continue
|
||||
update_model, _ = _resolve_enum([
|
||||
"NXOpen.Sketch.UpdateLevel.Model",
|
||||
"NXOpen.UpdateLevel.Model",
|
||||
])
|
||||
if update_model is None:
|
||||
update_model = 1 # numeric fallback
|
||||
update_model = 1
|
||||
sketch.Deactivate(view_false, update_model)
|
||||
except Exception as exc:
|
||||
lister.WriteLine(f"[import] Warning deactivating: {exc}")
|
||||
|
||||
lister.WriteLine(f"[import] Done: {total_lines} total line segments in sketch '{sketch_name}'")
|
||||
lister.WriteLine(f"[import] Outer boundary: {len(outer_2d)} pts")
|
||||
lister.WriteLine(f"[import] Pockets: {len(pockets)}")
|
||||
lister.WriteLine(f"[import] Pockets: {len(pockets)}")
|
||||
|
||||
lister.WriteLine("\n" + "=" * 60)
|
||||
lister.WriteLine(" Import complete — extrude the sketch to rib thickness")
|
||||
|
||||
Reference in New Issue
Block a user