feat(import_profile): auto-extrude after sketch creation

Full cycle now automated:
1. Delete old extrude (if exists)
2. Delete old sketch (try ReplaceFeatureBuilder first, fallback to delete)
3. Create new sketch with rib geometry
4. Extrude new sketch by rib thickness along face normal
5. Name both features for identification on next iteration

Rib thickness read from profile JSON (parameters_used.thickness)
with fallback to geometry JSON or default 10mm.

No more manual extrude step needed between iterations.
This commit is contained in:
2026-02-17 00:40:00 +00:00
parent 515eef145f
commit ecba40f189

View File

@@ -31,6 +31,8 @@ from typing import Any, Dict, List, Tuple
# ---------------------------------------------------------------------------
SKETCH_NAME_PREFIX = "ISOGRID_RIB_" # e.g., ISOGRID_RIB_sandbox_1
EXTRUDE_NAME_PREFIX = "ISOGRID_EXTRUDE_" # e.g., ISOGRID_EXTRUDE_sandbox_1
DEFAULT_RIB_THICKNESS = 10.0 # mm, fallback if not in profile JSON
# ---------------------------------------------------------------------------
@@ -279,10 +281,25 @@ def _find_or_create_sketch(
lister.WriteLine(f"[import] Created sketch: {sketch_name}")
# If old sketch existed, replace references and delete it
if old_feature is not None and new_feature is not None:
lister.WriteLine(f"[import] Replacing old sketch with new one...")
_replace_and_delete_old_sketch(part, old_feature, new_feature, lister)
# If old sketch existed, delete it (extrude will be recreated by _extrude_sketch)
if old_feature is not None:
import NXOpen as _NXOpen
_session = _NXOpen.Session.GetSession()
# First try ReplaceFeatureBuilder to preserve downstream references
replaced = False
if new_feature is not None:
lister.WriteLine(f"[import] Attempting ReplaceFeatureBuilder (old → new)...")
try:
_replace_and_delete_old_sketch(part, old_feature, new_feature, lister)
replaced = True
except Exception as exc:
lister.WriteLine(f"[import] ReplaceFeatureBuilder failed: {exc}")
# Fallback: just delete old sketch (extrude will be re-created)
if not replaced:
lister.WriteLine(f"[import] Deleting old sketch (extrude will be re-created)...")
_delete_feature(_session, old_feature, lister, f"old sketch {sketch_name}")
return sketch
@@ -416,6 +433,205 @@ def _draw_structured_pocket(part, pocket, transform, lister):
return lines_drawn, arcs_drawn
# ---------------------------------------------------------------------------
# Extrude sketch
# ---------------------------------------------------------------------------
def _find_extrude_feature(part: Any, extrude_name: str, lister: Any):
"""Find an existing extrude feature by name. Returns Feature or None."""
try:
for feat in part.Features:
try:
if feat.Name == extrude_name:
lister.WriteLine(f"[import] Found existing extrude: {extrude_name}")
return feat
except Exception:
continue
except Exception:
pass
return None
def _delete_feature(session: Any, feature: Any, lister: Any, label: str = "feature"):
"""Delete a feature using the update manager."""
import NXOpen
try:
mark = session.SetUndoMark(NXOpen.Session.MarkVisibility.Visible,
f"Delete {label}")
session.UpdateManager.AddToDeleteList(feature)
session.UpdateManager.DoUpdate(mark)
lister.WriteLine(f"[import] Deleted {label}: {feature.JournalIdentifier}")
return True
except Exception as exc:
lister.WriteLine(f"[import] WARNING: Could not delete {label}: {exc}")
return False
def _extrude_sketch(part: Any, sketch: Any, thickness: float,
normal: List[float], extrude_name: str, lister: Any):
"""
Extrude all curves in the sketch by `thickness` along the face normal.
Creates a new Extrude feature named `extrude_name`.
If an old extrude with the same name exists, delete it first.
"""
import NXOpen
session = NXOpen.Session.GetSession()
# Delete old extrude if it exists
old_extrude = _find_extrude_feature(part, extrude_name, lister)
if old_extrude is not None:
_delete_feature(session, old_extrude, lister, f"old extrude {extrude_name}")
lister.WriteLine(f"[import] Extruding sketch by {thickness} mm along normal")
mark = session.SetUndoMark(NXOpen.Session.MarkVisibility.Visible,
f"Extrude {extrude_name}")
try:
# Create ExtrudeBuilder (null feature = new extrude)
builder = part.Features.CreateExtrudeBuilder(
NXOpen.Features.Feature.Null
)
# Set the section: use sketch curves
# Get all curves from the sketch
sketch_feature = sketch.Feature
curves = []
try:
# Try getting sketch geometry objects
geom = sketch.GetAllGeometry()
for obj in geom:
curves.append(obj)
except Exception:
pass
if not curves:
# Fallback: get curves from feature's entities
try:
entities = sketch_feature.GetEntities()
for ent in entities:
if hasattr(ent, 'StartPoint') or hasattr(ent, 'GetLength'):
curves.append(ent)
except Exception:
pass
lister.WriteLine(f"[import] Found {len(curves)} curves in sketch for extrude")
# Create section and add curves
section = part.Sections.CreateSection(
0.0095, # chaining tolerance
0.01, # distance tolerance
0.5, # angle tolerance
)
section.SetAllowedEntityTypes(
NXOpen.Section.AllowTypes.OnlyCurves
)
# Add sketch curves to the section
sketch_feature_tag = sketch.Feature
curves_added = False
# Method 1: Add the sketch feature directly as section input
try:
feature_rule = part.ScRuleFactory.CreateRuleFeatureCurves(
[sketch_feature_tag]
)
section.AllowSelfIntersection(True)
help_pt = NXOpen.Point3d(0, 0, 0)
section.AddToSection(
[feature_rule],
NXOpen.NXObject.Null, # seed
NXOpen.NXObject.Null, # start connector
NXOpen.NXObject.Null, # end connector
help_pt,
NXOpen.Section.Mode.Create,
False, # go to end
)
curves_added = True
lister.WriteLine("[import] Added sketch curves to section via FeatureCurves rule")
except Exception as exc:
lister.WriteLine(f"[import] FeatureCurves rule failed: {exc}")
if not curves_added and curves:
# Method 2: Add individual curves
try:
curve_rule = part.ScRuleFactory.CreateRuleCurveDumb(curves)
help_pt = NXOpen.Point3d(0, 0, 0)
section.AddToSection(
[curve_rule],
curves[0],
NXOpen.NXObject.Null,
NXOpen.NXObject.Null,
help_pt,
NXOpen.Section.Mode.Create,
False,
)
curves_added = True
lister.WriteLine("[import] Added curves via CurveDumb rule")
except Exception as exc:
lister.WriteLine(f"[import] CurveDumb rule failed: {exc}")
if not curves_added:
lister.WriteLine("[import] ERROR: Could not add curves to extrude section")
builder.Destroy()
return None
builder.Section = section
# Set direction (along face normal)
direction = part.Directions.CreateDirection(
NXOpen.Point3d(0, 0, 0),
NXOpen.Vector3d(normal[0], normal[1], normal[2]),
NXOpen.SmartObject.UpdateOption.WithinModeling,
)
builder.Direction = direction
# Set limits: start=0, end=thickness
builder.Limits.StartExtend.Value.RightHandSide = "0"
builder.Limits.EndExtend.Value.RightHandSide = str(thickness)
# Boolean: create (no unite/subtract for now — idealized model)
builder.BooleanOperation.Type = NXOpen.GeometricUtilities.BooleanOperation.BooleanType.Create
# Commit
extrude_obj = builder.Commit()
builder.Destroy()
# Rename the extrude feature
committed = builder.GetCommittedObjects() if hasattr(builder, 'GetCommittedObjects') else []
extrude_feature = None
if isinstance(extrude_obj, NXOpen.Features.Feature):
extrude_feature = extrude_obj
else:
# Find the most recently created extrude feature
for feat in part.Features:
try:
if "Extrude" in feat.FeatureType and feat.Name == "":
extrude_feature = feat
except Exception:
continue
if extrude_feature is not None:
try:
extrude_feature.SetName(extrude_name)
lister.WriteLine(f"[import] Extrude created and named: {extrude_name}")
except Exception:
lister.WriteLine(f"[import] Extrude created (could not rename)")
else:
lister.WriteLine(f"[import] Extrude committed (feature reference unclear)")
return extrude_feature
except Exception as exc:
lister.WriteLine(f"[import] ERROR creating extrude: {exc}")
import traceback
lister.WriteLine(traceback.format_exc())
return None
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
@@ -588,8 +804,25 @@ def main():
lister.WriteLine(f"[import] Pockets: {len(pockets)}")
# --- Auto-extrude ---
rib_thickness = profile.get("parameters_used", {}).get("thickness",
geometry.get("thickness", DEFAULT_RIB_THICKNESS))
if rib_thickness is None:
rib_thickness = DEFAULT_RIB_THICKNESS
normal = transform["normal"]
extrude_name = EXTRUDE_NAME_PREFIX + sandbox_id
extrude_feat = _extrude_sketch(
work_part, sketch, rib_thickness, normal, extrude_name, lister
)
if extrude_feat is not None:
lister.WriteLine(f"[import] ✓ Sketch + Extrude complete for {sandbox_id}")
else:
lister.WriteLine(f"[import] ⚠ Sketch created but extrude failed — extrude manually")
lister.WriteLine(f"[import] Thickness: {rib_thickness} mm along normal {normal}")
lister.WriteLine("\n" + "=" * 60)
lister.WriteLine(" Import complete — extrude the sketch to rib thickness")
lister.WriteLine(" Import complete")
lister.WriteLine("=" * 60)