diff --git a/tools/adaptive-isogrid/src/nx/import_profile.py b/tools/adaptive-isogrid/src/nx/import_profile.py index 2dce7f41..ff22f39c 100644 --- a/tools/adaptive-isogrid/src/nx/import_profile.py +++ b/tools/adaptive-isogrid/src/nx/import_profile.py @@ -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)