feat(isogrid): Add update-in-place NX import + 3 density field variations

Major improvements to NX import workflow and rib pattern generation:

**NX Import (import_profile.py)**
- Smart sketch management: detects existing sketches and updates in-place
- Preserves extrude references (no manual re-reference needed!)
- First run: creates new sketch + auto-extrude
- Subsequent runs: clears geometry, redraws, extrude regenerates automatically
- Added _find_sketch_by_name() and _clear_sketch_geometry() functions

**Rib Pattern Variations**
Generated 3 different density field strategies for testing NX updates:
- Balanced (α=1.0, β=0.3): Original moderate density - 86 pockets, 2,499g
- Edge-focused (α=0.3, β=1.5): Dense ribs near boundaries - 167 pockets, 2,328g
- Hole-focused (α=1.8, β=0.15): Dense around holes - 62 pockets, 3,025g

**New Files**
- import_profile_update_test.py: Standalone update-only test script
- params_large_triangles.json: s_min=30mm, s_max=100mm (larger triangles)
- params_edge_focused.json: β=1.5 (boundary reinforcement)
- params_hole_focused.json: α=1.8 (hole reinforcement)
- sandbox_results/{edge_focused,hole_focused}/: Complete rib profile sets

**Test Results (Sandbox 1)**
- 833 triangles with large triangle params (vs 1,501 with previous params)
- Edge-focused: 1,155 triangles, 167 pockets (2x denser)
- Hole-focused: 523 triangles, 62 pockets (sparse pattern)

This enables rapid rib pattern iteration in NX without losing extrude references!

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-17 22:02:41 -05:00
parent 6ed074dbbf
commit 8efa8ba0d1
15 changed files with 83878 additions and 88 deletions

View File

@@ -1,13 +1,17 @@
"""
NXOpen script — Import rib profile into NX as a sketch.
NXOpen script — Import/Update 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:
or updates an NX sketch on the sandbox plane containing:
- Outer boundary polyline
- Structured pocket geometry (3 lines + 3 arcs per triangular pocket)
- Hole circles (bolt holes)
The sketch is placed in the idealized part. Antoine extrudes manually the first
time; subsequent iterations only update the sketch and the extrude regenerates.
Smart sketch management:
- FIRST RUN: Creates new sketch + auto-generates extrude
- SUBSEQUENT RUNS: Updates existing sketch IN-PLACE (preserves extrude reference!)
This means you can iterate on rib patterns without manually re-referencing the extrude!
Usage (NX Journal):
File > Execute > NX Journal > import_profile.py
@@ -17,6 +21,7 @@ created by extract_sandbox.py (next to the idealized part).
Author: Atomizer / Adaptive Isogrid
Created: 2026-02-16
Updated: 2026-02-17 (Update-in-place mode added)
"""
from __future__ import annotations
@@ -74,20 +79,21 @@ def unproject_point_to_3d(
# ---------------------------------------------------------------------------
# NX sketch creation
# NX sketch creation and update
# ---------------------------------------------------------------------------
def _find_old_sketch_feature(part: Any, sketch_name: str, lister: Any):
def _find_sketch_by_name(part: Any, sketch_name: str, lister: Any):
"""
Find an existing sketch feature by Name attribute.
Returns the Feature object (not Sketch), or None.
Find an existing sketch by name.
Returns the Sketch object (not Feature), or None.
"""
try:
for feat in part.Features:
for sketch in part.Sketches:
try:
if feat.Name == sketch_name:
lister.WriteLine(f"[import] Found existing sketch feature: {sketch_name} ({feat.JournalIdentifier})")
return feat
# Check if the sketch's feature has the matching name
if sketch.Feature and sketch.Feature.Name == sketch_name:
lister.WriteLine(f"[import] Found existing sketch: {sketch_name}")
return sketch
except Exception:
continue
except Exception:
@@ -95,67 +101,36 @@ def _find_old_sketch_feature(part: Any, sketch_name: str, lister: Any):
return None
def _replace_and_delete_old_sketch(part: Any, old_feature: Any, new_feature: Any, lister: Any):
def _clear_sketch_geometry(sketch: Any, lister: Any):
"""
Replace old sketch with new one using NX's ReplaceFeatureBuilder.
Delete all geometry (curves and points) from an active sketch.
Sketch must be activated before calling this.
"""
import NXOpen
import NXOpen.Features
session = NXOpen.Session.GetSession()
mark = session.SetUndoMark(NXOpen.Session.MarkVisibility.Visible,
"Replace isogrid sketch")
try:
# Create the ReplaceFeatureBuilder (takes no args)
builder = part.Features.CreateReplaceFeatureBuilder()
# Get all curves and points in the sketch
geom_objects = sketch.GetAllGeometry()
# Set the original feature (old sketch) to be replaced
added_old = builder.SelectFeature.Add(old_feature)
lister.WriteLine(f"[import] Set original feature: {old_feature.JournalIdentifier}")
if len(geom_objects) == 0:
lister.WriteLine("[import] Sketch is already empty")
return
# Set the replacement feature (new sketch)
added_new = builder.ReplacementFeature.Add(new_feature)
lister.WriteLine(f"[import] Set replacement feature: {new_feature.JournalIdentifier}")
lister.WriteLine(f"[import] Clearing {len(geom_objects)} existing curves/points...")
# Configuration
builder.DeleteOriginalFeature = True
builder.DoAutomaticGeomMatch = True
# Add all geometry to delete list
session.UpdateManager.AddObjectsToDeleteList(geom_objects)
# Run automatic geometry matching (maps old curves → new curves)
try:
builder.AutomatchMap()
lister.WriteLine("[import] Auto-match mapping completed")
except Exception as exc:
lister.WriteLine(f"[import] Auto-match warning (may still work): {exc}")
# Execute the deletion
mark = session.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Clear sketch geometry")
nErrs = session.UpdateManager.DoUpdate(mark)
# Update the mapping
try:
builder.UpdateMap()
except Exception as exc:
lister.WriteLine(f"[import] UpdateMap note: {exc}")
# Commit the replacement
builder.Commit()
lister.WriteLine("[import] ReplaceFeature committed — all references updated")
builder.Destroy()
lister.WriteLine(f"[import] Cleared sketch geometry ({nErrs} warnings)")
except Exception as exc:
lister.WriteLine(f"[import] ReplaceFeatureBuilder failed: {exc}")
lister.WriteLine("[import] Falling back to manual delete + re-reference...")
# Fallback: just delete the old sketch
# Downstream features (extrude) will go into "out of date" state
# and need manual re-selection of the new sketch section
try:
nErrs = session.UpdateManager.AddToDeleteList(old_feature)
nErrs2 = session.UpdateManager.DoUpdate(mark)
lister.WriteLine(f"[import] Deleted old sketch ({nErrs + nErrs2} warnings)")
lister.WriteLine("[import] ⚠ Extrude may need manual sketch re-selection")
except Exception as exc2:
lister.WriteLine(f"[import] Delete also failed: {exc2}")
lister.WriteLine("[import] ⚠ Manually delete old sketch and fix extrude")
lister.WriteLine(f"[import] Warning during geometry clear: {exc}")
def _find_or_create_sketch(
@@ -165,17 +140,23 @@ def _find_or_create_sketch(
lister: Any,
) -> Any:
"""
Always create a new sketch on the sandbox plane.
After creation, rename the sketch feature to sketch_name.
If an old sketch with the same name exists, replace references and delete it.
Returns the (new) Sketch object.
Smart sketch management: updates existing sketch in-place if found,
otherwise creates new sketch. This preserves downstream references (extrudes).
Returns the Sketch object.
"""
import NXOpen
# Check for existing sketch with this name (will be replaced later)
old_feature = _find_old_sketch_feature(part, sketch_name, lister)
# Check if sketch already exists
existing_sketch = _find_sketch_by_name(part, sketch_name, lister)
lister.WriteLine(f"[import] Creating new sketch: {sketch_name}")
if existing_sketch is not None:
# UPDATE MODE: Sketch exists, we'll update it in-place
lister.WriteLine(f"[import] UPDATE mode: Sketch exists, will update in-place")
lister.WriteLine(f"[import] This preserves extrude references ✓")
return existing_sketch
# CREATE MODE: Sketch doesn't exist, create new one
lister.WriteLine(f"[import] CREATE mode: Creating new sketch: {sketch_name}")
origin = transform["origin"]
normal = transform["normal"]
@@ -266,28 +247,7 @@ def _find_or_create_sketch(
except Exception:
pass
lister.WriteLine(f"[import] Created sketch: {sketch_name}")
# 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}")
lister.WriteLine(f"[import] Created new sketch: {sketch_name}")
return sketch
@@ -876,6 +836,10 @@ def main():
lister.WriteLine(f"[import] ERROR activating sketch: {exc}")
continue
# If updating existing sketch, clear old geometry first
# (This preserves the sketch object → extrude reference stays valid!)
_clear_sketch_geometry(sketch, lister)
# --- Draw geometry ---
# Strategy: draw outer boundary + pocket outlines (lines+arcs) + holes
# NX sketch regions: the rib web is the area BETWEEN outer boundary

View File

@@ -0,0 +1,539 @@
"""
NXOpen script — UPDATE existing isogrid rib sketch (preserves extrude references).
This test script updates an existing sketch in-place instead of creating a new one.
This preserves downstream feature references (extrudes, patterns, etc.).
Usage:
1. Ensure your sketch exists with name: ISOGRID_RIB_sandbox_1
2. Copy rib_profile_sandbox_1.json + geometry_sandbox_1.json to adaptive_isogrid_data/
3. Run this script: File > Execute > NX Journal
4. Extrude should update automatically (no re-reference needed!)
Workflow:
- Find existing sketch by name
- Activate sketch
- Delete all existing geometry
- Draw new geometry from JSON
- Deactivate sketch
- Sketch object stays the same → extrude reference preserved ✓
Author: Atomizer / Adaptive Isogrid
Created: 2026-02-17
"""
from __future__ import annotations
import json
import math
import os
from typing import Any, Dict, List, Tuple
# ---------------------------------------------------------------------------
# Config
# ---------------------------------------------------------------------------
SKETCH_NAME_PREFIX = "ISOGRID_RIB_" # e.g., ISOGRID_RIB_sandbox_1
# ---------------------------------------------------------------------------
# Geometry helpers (same as import_profile.py)
# ---------------------------------------------------------------------------
def unproject_to_3d(
points2d: List[List[float]],
transform: Dict[str, List[float]],
) -> List[Tuple[float, float, float]]:
"""Convert 2D profile points back to 3D using the extraction transform."""
ox, oy, oz = transform["origin"]
xx, xy, xz = transform["x_axis"]
yx, yy, yz = transform["y_axis"]
pts3d = []
for x, y in points2d:
px = ox + x * xx + y * yx
py = oy + x * xy + y * yy
pz = oz + x * xz + y * yz
pts3d.append((px, py, pz))
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,
)
# ---------------------------------------------------------------------------
# Sketch finding and clearing
# ---------------------------------------------------------------------------
def _find_sketch_by_name(part: Any, sketch_name: str, lister: Any):
"""
Find an existing sketch by name.
Returns the Sketch object (not Feature), or None.
"""
try:
for sketch in part.Sketches:
try:
# Check if the sketch's feature has the matching name
if sketch.Feature and sketch.Feature.Name == sketch_name:
lister.WriteLine(f"[update] Found existing sketch: {sketch_name}")
return sketch
except Exception:
continue
except Exception:
pass
return None
def _clear_sketch_geometry(sketch: Any, lister: Any):
"""
Delete all geometry (curves and points) from an active sketch.
Sketch must be activated before calling this.
"""
import NXOpen
session = NXOpen.Session.GetSession()
work_part = session.Parts.Work
try:
# Get all curves and points in the sketch
geom_objects = sketch.GetAllGeometry()
if len(geom_objects) == 0:
lister.WriteLine("[update] Sketch is already empty")
return
lister.WriteLine(f"[update] Deleting {len(geom_objects)} existing curves/points...")
# Add all geometry to delete list
session.UpdateManager.AddObjectsToDeleteList(geom_objects)
# Execute the deletion
mark = session.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Clear sketch geometry")
nErrs = session.UpdateManager.DoUpdate(mark)
lister.WriteLine(f"[update] Cleared sketch geometry ({nErrs} warnings)")
except Exception as exc:
lister.WriteLine(f"[update] Warning during geometry clear: {exc}")
# ---------------------------------------------------------------------------
# Sketch drawing: lines and arcs (same as import_profile.py)
# ---------------------------------------------------------------------------
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
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_segment(part, seg, transform, lister):
"""
Draw a single typed segment (line or arc) in the active sketch.
Supports schema v2.0 segment dicts with "type", "start", "end", "center", "radius".
Returns ("line"|"arc", success_bool).
"""
seg_type = seg.get("type", "line")
start_3d = unproject_point_to_3d(seg["start"], transform)
end_3d = unproject_point_to_3d(seg["end"], transform)
if seg_type == "arc" and "center" in seg:
center_3d = unproject_point_to_3d(seg["center"], transform)
radius = seg["radius"]
# Prefer the sampled midpoint from NX (avoids direction ambiguity)
if "mid" in seg:
mid_2d = seg["mid"]
else:
# Fallback: compute from clockwise flag
cx, cy = seg["center"]
sx, sy = seg["start"]
ex, ey = seg["end"]
sa = math.atan2(sy - cy, sx - cx)
ea = math.atan2(ey - cy, ex - cx)
clockwise = seg.get("clockwise", False)
if clockwise:
da = sa - ea
if da <= 0:
da += 2 * math.pi
mid_angle = sa - da / 2.0
else:
da = ea - sa
if da <= 0:
da += 2 * math.pi
mid_angle = sa + da / 2.0
mid_2d = [cx + radius * math.cos(mid_angle), cy + radius * math.sin(mid_angle)]
mid_3d = unproject_point_to_3d(mid_2d, transform)
try:
_draw_arc_3pt(part, start_3d, mid_3d, end_3d)
return ("arc", True)
except Exception as exc:
lister.WriteLine(f"[update] Arc failed, fallback to line: {exc}")
# Fallback: draw as line
try:
_draw_line(part, start_3d, end_3d)
return ("line", True)
except Exception:
return ("line", False)
else:
try:
_draw_line(part, start_3d, end_3d)
return ("line", True)
except Exception:
return ("line", False)
def _draw_outer_boundary(part, outer_boundary, transform, lister):
"""
Draw the outer boundary. Handles both:
- Schema v2.0: list of typed segment dicts
- Schema v1.0: list of [x,y] points (polyline)
Returns (num_lines, num_arcs).
"""
if not outer_boundary:
return 0, 0
# Detect schema version: v2.0 segments are dicts with "type" key
if isinstance(outer_boundary[0], dict) and "type" in outer_boundary[0]:
# Schema v2.0 — typed segments
n_lines = 0
n_arcs = 0
for seg in outer_boundary:
kind, ok = _draw_segment(part, seg, transform, lister)
if ok:
if kind == "arc":
n_arcs += 1
else:
n_lines += 1
return n_lines, n_arcs
else:
# Schema v1.0 — flat polyline points
outer_3d = unproject_to_3d(outer_boundary, transform)
n = len(outer_3d)
if n < 2:
return 0, 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):
try:
_draw_line(part, outer_3d[i], outer_3d[(i + 1) % n])
count += 1
except Exception as exc:
if count == 0:
lister.WriteLine(f"[update] ERROR: First line failed: {exc}")
return 0, 0
return count, 0
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"[update] 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("[update] Arc fallback: 2 lines")
except Exception:
pass
return lines_drawn, arcs_drawn
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
return None, None
# ---------------------------------------------------------------------------
# Main update logic
# ---------------------------------------------------------------------------
def main():
import NXOpen
session = NXOpen.Session.GetSession()
lister = session.ListingWindow
lister.Open()
lister.WriteLine("=" * 60)
lister.WriteLine(" Adaptive Isogrid — UPDATE Existing Sketch Test")
lister.WriteLine("=" * 60)
# Navigate to idealized part
work_part = session.Parts.Work
part_name = work_part.Name if hasattr(work_part, "Name") else ""
lister.WriteLine(f"[update] Work part: {part_name}")
if not part_name.endswith("_i"):
for part in session.Parts:
pname = part.Name if hasattr(part, "Name") else ""
if pname.endswith("_i"):
session.Parts.SetWork(part)
work_part = part
lister.WriteLine(f"[update] Switched to idealized part: {pname}")
break
try:
part_dir = os.path.dirname(work_part.FullPath)
except Exception:
part_dir = os.getcwd()
data_dir = os.path.join(part_dir, "adaptive_isogrid_data")
if not os.path.isdir(data_dir):
lister.WriteLine(f"[update] ERROR: Data directory not found: {data_dir}")
return
# 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:
lister.WriteLine(f"[update] ERROR: No rib_profile*.json in {data_dir}")
return
lister.WriteLine(f"[update] Found {len(profile_files)} profile(s) to update")
for profile_file in profile_files:
profile_path = os.path.join(data_dir, profile_file)
sandbox_id = profile_file.replace("rib_profile_", "").replace(".json", "")
if not sandbox_id.startswith("sandbox"):
sandbox_id = "sandbox_1"
geom_path = os.path.join(data_dir, f"geometry_{sandbox_id}.json")
if not os.path.exists(geom_path):
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("[update] ERROR: No geometry JSON for transform")
continue
lister.WriteLine(f"\n--- Updating {profile_file} ---")
lister.WriteLine(f"[update] Geometry (transform): {geom_path}")
try:
with open(profile_path, "r") as f:
profile = json.load(f)
with open(geom_path, "r") as f:
geometry = json.load(f)
except Exception as exc:
lister.WriteLine(f"[update] ERROR reading JSON: {exc}")
continue
transform = geometry["transform"]
sketch_name = SKETCH_NAME_PREFIX + sandbox_id
# CRITICAL: Find existing sketch (don't create new)
sketch = _find_sketch_by_name(work_part, sketch_name, lister)
if sketch is None:
lister.WriteLine(f"[update] ERROR: Sketch not found: {sketch_name}")
lister.WriteLine("[update] Create the sketch first using import_profile.py")
continue
lister.WriteLine(f"[update] Updating existing sketch: {sketch_name}")
# Activate sketch
try:
view_false, path = _resolve_enum([
"NXOpen.Sketch.ViewReorient.FalseValue",
"NXOpen.ViewReorient.FalseValue",
])
if view_false is None:
view_false = False
sketch.Activate(view_false)
lister.WriteLine("[update] Sketch activated")
except Exception as exc:
lister.WriteLine(f"[update] ERROR activating sketch: {exc}")
continue
# Clear existing geometry
_clear_sketch_geometry(sketch, lister)
# --- Draw new geometry (same as import_profile.py) ---
pockets = profile.get("pockets", [])
outer_2d = profile.get("outer_boundary", [])
# 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])
if is_structured:
lister.WriteLine(f"[update] Drawing {len(pockets)} structured pockets + outer boundary")
# Outer boundary
outer_lines, outer_arcs = _draw_outer_boundary(work_part, outer_2d, transform, lister)
lister.WriteLine(f"[update] Outer boundary: {outer_lines} lines + {outer_arcs} arcs")
# Pocket outlines
total_lines = outer_lines
total_arcs = outer_arcs
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"[update] ... {idx+1}/{len(pockets)} pockets drawn")
lister.WriteLine(f"[update] Pockets done: {total_lines} lines + {total_arcs} arcs")
# Holes (bolt circles)
holes = profile.get("holes", [])
if holes:
holes_drawn = 0
for hole in holes:
try:
cx, cy = hole["center"]
r = hole.get("radius", hole.get("diameter", 0) / 2.0)
p1 = [cx + r, cy]
p2 = [cx, cy + r]
p3 = [cx - r, cy]
p4 = [cx, cy - r]
p1_3d = unproject_point_to_3d(p1, transform)
p2_3d = unproject_point_to_3d(p2, transform)
p3_3d = unproject_point_to_3d(p3, transform)
p4_3d = unproject_point_to_3d(p4, transform)
_draw_arc_3pt(work_part, p1_3d, p2_3d, p3_3d)
_draw_arc_3pt(work_part, p3_3d, p4_3d, p1_3d)
holes_drawn += 1
total_arcs += 2
except Exception as exc:
lister.WriteLine(f"[update] WARN: hole failed: {exc}")
lister.WriteLine(f"[update] Holes: {holes_drawn}/{len(holes)} drawn ({holes_drawn*2} arcs)")
lister.WriteLine(f"[update] Total: {total_lines} lines + {total_arcs} arcs")
else:
# Legacy format
lister.WriteLine(f"[update] Legacy format: {len(pockets)} pocket polylines")
outer_lines, outer_arcs = _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"[update] Done: {total_lines} lines in sketch")
# Deactivate sketch
try:
update_model, _ = _resolve_enum([
"NXOpen.Sketch.UpdateLevel.Model",
"NXOpen.UpdateLevel.Model",
])
if update_model is None:
update_model = 1
sketch.Deactivate(view_false, update_model)
lister.WriteLine("[update] Sketch deactivated and updated")
except Exception as exc:
lister.WriteLine(f"[update] Warning deactivating: {exc}")
lister.WriteLine(f"[update] ✓ Sketch updated: {sketch_name}")
lister.WriteLine(f"[update] Extrude should update automatically!")
lister.WriteLine("\n" + "=" * 60)
lister.WriteLine(" Update complete")
lister.WriteLine("=" * 60)
main()

View File

@@ -0,0 +1,19 @@
{
"eta_0": 0.05,
"alpha": 0.3,
"R_0": 40.0,
"kappa": 1.0,
"p": 2.0,
"beta": 1.5,
"R_edge": 25.0,
"s_min": 25.0,
"s_max": 80.0,
"t_min": 2.5,
"t_0": 3.5,
"gamma": 1.0,
"w_frame": 3.0,
"r_f": 1.5,
"d_keep": 0.8,
"min_pocket_radius": 8.0,
"min_triangle_area": 30.0
}

View File

@@ -0,0 +1,19 @@
{
"eta_0": 0.05,
"alpha": 1.8,
"R_0": 50.0,
"kappa": 1.0,
"p": 2.0,
"beta": 0.15,
"R_edge": 10.0,
"s_min": 20.0,
"s_max": 90.0,
"t_min": 2.5,
"t_0": 3.5,
"gamma": 1.0,
"w_frame": 3.0,
"r_f": 1.5,
"d_keep": 0.8,
"min_pocket_radius": 8.0,
"min_triangle_area": 30.0
}

View File

@@ -0,0 +1,19 @@
{
"eta_0": 0.1,
"alpha": 1.0,
"R_0": 40.0,
"kappa": 1.0,
"p": 2.0,
"beta": 0.3,
"R_edge": 15.0,
"s_min": 30.0,
"s_max": 100.0,
"t_min": 2.5,
"t_0": 3.5,
"gamma": 1.0,
"w_frame": 3.0,
"r_f": 1.5,
"d_keep": 0.8,
"min_pocket_radius": 8.0,
"min_triangle_area": 30.0
}

View File

@@ -0,0 +1,795 @@
{
"schema_version": "2.0",
"units": "mm",
"sandbox_id": "sandbox_1",
"outer_boundary": [
{
"type": "line",
"start": [
381.787159,
14.92177
],
"end": [
132.687159,
14.92177
]
},
{
"type": "line",
"start": [
132.687159,
14.92177
],
"end": [
132.687159,
-13.57823
]
},
{
"type": "line",
"start": [
132.687159,
-13.57823
],
"end": [
88.687159,
-13.57823
]
},
{
"type": "line",
"start": [
88.687159,
-13.57823
],
"end": [
88.687159,
14.92177
]
},
{
"type": "line",
"start": [
88.687159,
14.92177
],
"end": [
-13.412841,
14.92177
]
},
{
"type": "line",
"start": [
-13.412841,
14.92177
],
"end": [
-13.412841,
0.02177
]
},
{
"type": "line",
"start": [
-13.412841,
0.02177
],
"end": [
-30.812841,
0.02177
]
},
{
"type": "line",
"start": [
-30.812841,
0.02177
],
"end": [
-30.812841,
-254.17823
]
},
{
"type": "line",
"start": [
-30.812841,
-254.17823
],
"end": [
169.435852,
-254.17823
]
},
{
"type": "line",
"start": [
169.435852,
-254.17823
],
"end": [
169.435852,
-417.57823
]
},
{
"type": "line",
"start": [
169.435852,
-417.57823
],
"end": [
197.121675,
-417.57823
]
},
{
"type": "line",
"start": [
197.121675,
-417.57823
],
"end": [
197.121675,
-401.57823
]
},
{
"type": "line",
"start": [
197.121675,
-401.57823
],
"end": [
212.121675,
-401.57823
]
},
{
"type": "line",
"start": [
212.121675,
-401.57823
],
"end": [
212.121675,
-417.57823
]
},
{
"type": "line",
"start": [
212.121675,
-417.57823
],
"end": [
289.687159,
-417.57823
]
},
{
"type": "line",
"start": [
289.687159,
-417.57823
],
"end": [
304.687159,
-406.57823
]
},
{
"type": "line",
"start": [
304.687159,
-406.57823
],
"end": [
317.687159,
-406.57823
]
},
{
"type": "line",
"start": [
317.687159,
-406.57823
],
"end": [
332.687159,
-417.57823
]
},
{
"type": "line",
"start": [
332.687159,
-417.57823
],
"end": [
381.787159,
-417.57823
]
},
{
"type": "line",
"start": [
381.787159,
-417.57823
],
"end": [
381.787159,
-395.17823
]
},
{
"type": "line",
"start": [
381.787159,
-395.17823
],
"end": [
404.187159,
-395.17823
]
},
{
"type": "line",
"start": [
404.187159,
-395.17823
],
"end": [
404.187159,
-322.57823
]
},
{
"type": "line",
"start": [
404.187159,
-322.57823
],
"end": [
352.787159,
-322.57823
]
},
{
"type": "line",
"start": [
352.787159,
-322.57823
],
"end": [
352.787159,
-304.17823
]
},
{
"type": "line",
"start": [
352.787159,
-304.17823
],
"end": [
361.187159,
-304.17823
]
},
{
"type": "line",
"start": [
361.187159,
-304.17823
],
"end": [
361.187159,
-24.57823
]
},
{
"type": "line",
"start": [
361.187159,
-24.57823
],
"end": [
404.187159,
-24.57823
]
},
{
"type": "line",
"start": [
404.187159,
-24.57823
],
"end": [
404.187159,
0.02177
]
},
{
"type": "line",
"start": [
404.187159,
0.02177
],
"end": [
381.787159,
0.02177
]
},
{
"type": "line",
"start": [
381.787159,
0.02177
],
"end": [
381.787159,
14.92177
]
}
],
"inner_boundaries": [
{
"index": 0,
"segments": [
{
"type": "arc",
"start": [
0.0,
0.0
],
"end": [
0.0,
0.0
],
"center": [
0.0,
-3.07823
],
"radius": 3.07823,
"mid": [
0.0,
-6.15646
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 1,
"segments": [
{
"type": "arc",
"start": [
366.187159,
1.02177
],
"end": [
366.187159,
1.02177
],
"center": [
366.187159,
-3.07823
],
"radius": 4.1,
"mid": [
366.187159,
-7.17823
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 2,
"segments": [
{
"type": "arc",
"start": [
44.987159,
0.0
],
"end": [
44.987159,
0.0
],
"center": [
44.987159,
-3.07823
],
"radius": 3.07823,
"mid": [
44.987159,
-6.15646
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 3,
"segments": [
{
"type": "arc",
"start": [
250.707159,
-272.32823
],
"end": [
250.707159,
-272.32823
],
"center": [
250.707159,
-275.57823
],
"radius": 3.25,
"mid": [
250.707159,
-278.82823
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 4,
"segments": [
{
"type": "arc",
"start": [
44.987159,
-155.5
],
"end": [
44.987159,
-155.5
],
"center": [
44.987159,
-158.57823
],
"radius": 3.07823,
"mid": [
44.987159,
-161.65646
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 5,
"segments": [
{
"type": "arc",
"start": [
125.187159,
-232.47823
],
"end": [
125.187159,
-232.47823
],
"center": [
125.187159,
-236.57823
],
"radius": 4.1,
"mid": [
125.187159,
-240.67823
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 6,
"segments": [
{
"type": "arc",
"start": [
-9.812841,
-67.82823
],
"end": [
-9.812841,
-67.82823
],
"center": [
-9.812841,
-71.07823
],
"radius": 3.25,
"mid": [
-9.812841,
-74.32823
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 7,
"segments": [
{
"type": "arc",
"start": [
362.787159,
-372.9
],
"end": [
362.787159,
-372.9
],
"center": [
362.787159,
-375.97823
],
"radius": 3.07823,
"mid": [
362.787159,
-379.05646
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 8,
"segments": [
{
"type": "arc",
"start": [
250.707159,
-372.72823
],
"end": [
250.707159,
-372.72823
],
"center": [
250.707159,
-375.97823
],
"radius": 3.25,
"mid": [
250.707159,
-379.22823
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 9,
"segments": [
{
"type": "arc",
"start": [
291.457159,
-311.1
],
"end": [
291.457159,
-311.1
],
"center": [
291.457159,
-314.17823
],
"radius": 3.07823,
"mid": [
291.457159,
-317.25646
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 10,
"segments": [
{
"type": "arc",
"start": [
44.987159,
-68.0
],
"end": [
44.987159,
-68.0
],
"center": [
44.987159,
-71.07823
],
"radius": 3.07823,
"mid": [
44.987159,
-74.15646
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 11,
"segments": [
{
"type": "arc",
"start": [
194.447159,
-372.72823
],
"end": [
194.447159,
-372.72823
],
"center": [
194.447159,
-375.97823
],
"radius": 3.25,
"mid": [
194.447159,
-379.22823
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 12,
"segments": [
{
"type": "arc",
"start": [
291.457159,
-372.9
],
"end": [
291.457159,
-372.9
],
"center": [
291.457159,
-375.97823
],
"radius": 3.07823,
"mid": [
291.457159,
-379.05646
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 13,
"segments": [
{
"type": "arc",
"start": [
125.187159,
-154.47823
],
"end": [
125.187159,
-154.47823
],
"center": [
125.187159,
-158.57823
],
"radius": 4.1,
"mid": [
125.187159,
-162.67823
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 14,
"segments": [
{
"type": "arc",
"start": [
125.187159,
-66.97823
],
"end": [
125.187159,
-66.97823
],
"center": [
125.187159,
-71.07823
],
"radius": 4.1,
"mid": [
125.187159,
-75.17823
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 15,
"segments": [
{
"type": "arc",
"start": [
194.447159,
-272.32823
],
"end": [
194.447159,
-272.32823
],
"center": [
194.447159,
-275.57823
],
"radius": 3.25,
"mid": [
194.447159,
-278.82823
],
"clockwise": false
}
],
"num_segments": 1
}
],
"num_inner_boundaries": 16,
"thickness": null,
"transform": {
"origin": [
197.57823,
184.187159,
6.35
],
"x_axis": [
0.0,
-1.0,
0.0
],
"y_axis": [
1.0,
0.0,
-0.0
],
"normal": [
0.0,
0.0,
1.0
]
}
}

View File

@@ -0,0 +1,314 @@
{
"schema_version": "2.0",
"units": "mm",
"sandbox_id": "sandbox_2",
"outer_boundary": [
{
"type": "arc",
"start": [
0.0,
0.0
],
"end": [
7.5,
-7.5
],
"center": [
0.0,
-7.5
],
"radius": 7.5,
"mid": [
5.303301,
-2.196699
],
"clockwise": true
},
{
"type": "line",
"start": [
7.5,
-7.5
],
"end": [
7.5,
-22.6
]
},
{
"type": "line",
"start": [
7.5,
-22.6
],
"end": [
22.5,
-22.6
]
},
{
"type": "line",
"start": [
22.5,
-22.6
],
"end": [
22.5,
-13.496098
]
},
{
"type": "line",
"start": [
22.5,
-13.496098
],
"end": [
74.5,
-13.496098
]
},
{
"type": "line",
"start": [
74.5,
-13.496098
],
"end": [
74.5,
-22.6
]
},
{
"type": "line",
"start": [
74.5,
-22.6
],
"end": [
102.5,
-22.6
]
},
{
"type": "line",
"start": [
102.5,
-22.6
],
"end": [
102.5,
-7.5
]
},
{
"type": "arc",
"start": [
102.5,
-7.5
],
"end": [
117.5,
-7.5
],
"center": [
110.0,
-7.5
],
"radius": 7.5,
"mid": [
110.0,
0.0
],
"clockwise": false
},
{
"type": "line",
"start": [
117.5,
-7.5
],
"end": [
117.5,
-22.6
]
},
{
"type": "line",
"start": [
117.5,
-22.6
],
"end": [
140.748693,
-22.6
]
},
{
"type": "line",
"start": [
140.748693,
-22.6
],
"end": [
140.748693,
124.4
]
},
{
"type": "line",
"start": [
140.748693,
124.4
],
"end": [
117.5,
124.4
]
},
{
"type": "line",
"start": [
117.5,
124.4
],
"end": [
117.5,
102.5
]
},
{
"type": "arc",
"start": [
117.5,
102.5
],
"end": [
102.5,
102.5
],
"center": [
110.0,
102.5
],
"radius": 7.5,
"mid": [
110.0,
95.0
],
"clockwise": true
},
{
"type": "line",
"start": [
102.5,
102.5
],
"end": [
102.5,
124.4
]
},
{
"type": "line",
"start": [
102.5,
124.4
],
"end": [
7.5,
124.4
]
},
{
"type": "line",
"start": [
7.5,
124.4
],
"end": [
7.5,
102.5
]
},
{
"type": "arc",
"start": [
7.5,
102.5
],
"end": [
0.0,
95.0
],
"center": [
0.0,
102.5
],
"radius": 7.5,
"mid": [
5.303301,
97.196699
],
"clockwise": true
},
{
"type": "line",
"start": [
0.0,
95.0
],
"end": [
-13.5,
95.0
]
},
{
"type": "line",
"start": [
-13.5,
95.0
],
"end": [
-13.5,
0.0
]
},
{
"type": "line",
"start": [
-13.5,
0.0
],
"end": [
0.0,
0.0
]
}
],
"inner_boundaries": [],
"num_inner_boundaries": 0,
"thickness": null,
"transform": {
"origin": [
-196.0,
175.5,
4.35
],
"x_axis": [
0.0,
-1.0,
0.0
],
"y_axis": [
1.0,
0.0,
-0.0
],
"normal": [
0.0,
0.0,
1.0
]
}
}

View File

@@ -0,0 +1,795 @@
{
"schema_version": "2.0",
"units": "mm",
"sandbox_id": "sandbox_1",
"outer_boundary": [
{
"type": "line",
"start": [
381.787159,
14.92177
],
"end": [
132.687159,
14.92177
]
},
{
"type": "line",
"start": [
132.687159,
14.92177
],
"end": [
132.687159,
-13.57823
]
},
{
"type": "line",
"start": [
132.687159,
-13.57823
],
"end": [
88.687159,
-13.57823
]
},
{
"type": "line",
"start": [
88.687159,
-13.57823
],
"end": [
88.687159,
14.92177
]
},
{
"type": "line",
"start": [
88.687159,
14.92177
],
"end": [
-13.412841,
14.92177
]
},
{
"type": "line",
"start": [
-13.412841,
14.92177
],
"end": [
-13.412841,
0.02177
]
},
{
"type": "line",
"start": [
-13.412841,
0.02177
],
"end": [
-30.812841,
0.02177
]
},
{
"type": "line",
"start": [
-30.812841,
0.02177
],
"end": [
-30.812841,
-254.17823
]
},
{
"type": "line",
"start": [
-30.812841,
-254.17823
],
"end": [
169.435852,
-254.17823
]
},
{
"type": "line",
"start": [
169.435852,
-254.17823
],
"end": [
169.435852,
-417.57823
]
},
{
"type": "line",
"start": [
169.435852,
-417.57823
],
"end": [
197.121675,
-417.57823
]
},
{
"type": "line",
"start": [
197.121675,
-417.57823
],
"end": [
197.121675,
-401.57823
]
},
{
"type": "line",
"start": [
197.121675,
-401.57823
],
"end": [
212.121675,
-401.57823
]
},
{
"type": "line",
"start": [
212.121675,
-401.57823
],
"end": [
212.121675,
-417.57823
]
},
{
"type": "line",
"start": [
212.121675,
-417.57823
],
"end": [
289.687159,
-417.57823
]
},
{
"type": "line",
"start": [
289.687159,
-417.57823
],
"end": [
304.687159,
-406.57823
]
},
{
"type": "line",
"start": [
304.687159,
-406.57823
],
"end": [
317.687159,
-406.57823
]
},
{
"type": "line",
"start": [
317.687159,
-406.57823
],
"end": [
332.687159,
-417.57823
]
},
{
"type": "line",
"start": [
332.687159,
-417.57823
],
"end": [
381.787159,
-417.57823
]
},
{
"type": "line",
"start": [
381.787159,
-417.57823
],
"end": [
381.787159,
-395.17823
]
},
{
"type": "line",
"start": [
381.787159,
-395.17823
],
"end": [
404.187159,
-395.17823
]
},
{
"type": "line",
"start": [
404.187159,
-395.17823
],
"end": [
404.187159,
-322.57823
]
},
{
"type": "line",
"start": [
404.187159,
-322.57823
],
"end": [
352.787159,
-322.57823
]
},
{
"type": "line",
"start": [
352.787159,
-322.57823
],
"end": [
352.787159,
-304.17823
]
},
{
"type": "line",
"start": [
352.787159,
-304.17823
],
"end": [
361.187159,
-304.17823
]
},
{
"type": "line",
"start": [
361.187159,
-304.17823
],
"end": [
361.187159,
-24.57823
]
},
{
"type": "line",
"start": [
361.187159,
-24.57823
],
"end": [
404.187159,
-24.57823
]
},
{
"type": "line",
"start": [
404.187159,
-24.57823
],
"end": [
404.187159,
0.02177
]
},
{
"type": "line",
"start": [
404.187159,
0.02177
],
"end": [
381.787159,
0.02177
]
},
{
"type": "line",
"start": [
381.787159,
0.02177
],
"end": [
381.787159,
14.92177
]
}
],
"inner_boundaries": [
{
"index": 0,
"segments": [
{
"type": "arc",
"start": [
0.0,
0.0
],
"end": [
0.0,
0.0
],
"center": [
0.0,
-3.07823
],
"radius": 3.07823,
"mid": [
0.0,
-6.15646
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 1,
"segments": [
{
"type": "arc",
"start": [
366.187159,
1.02177
],
"end": [
366.187159,
1.02177
],
"center": [
366.187159,
-3.07823
],
"radius": 4.1,
"mid": [
366.187159,
-7.17823
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 2,
"segments": [
{
"type": "arc",
"start": [
44.987159,
0.0
],
"end": [
44.987159,
0.0
],
"center": [
44.987159,
-3.07823
],
"radius": 3.07823,
"mid": [
44.987159,
-6.15646
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 3,
"segments": [
{
"type": "arc",
"start": [
250.707159,
-272.32823
],
"end": [
250.707159,
-272.32823
],
"center": [
250.707159,
-275.57823
],
"radius": 3.25,
"mid": [
250.707159,
-278.82823
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 4,
"segments": [
{
"type": "arc",
"start": [
44.987159,
-155.5
],
"end": [
44.987159,
-155.5
],
"center": [
44.987159,
-158.57823
],
"radius": 3.07823,
"mid": [
44.987159,
-161.65646
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 5,
"segments": [
{
"type": "arc",
"start": [
125.187159,
-232.47823
],
"end": [
125.187159,
-232.47823
],
"center": [
125.187159,
-236.57823
],
"radius": 4.1,
"mid": [
125.187159,
-240.67823
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 6,
"segments": [
{
"type": "arc",
"start": [
-9.812841,
-67.82823
],
"end": [
-9.812841,
-67.82823
],
"center": [
-9.812841,
-71.07823
],
"radius": 3.25,
"mid": [
-9.812841,
-74.32823
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 7,
"segments": [
{
"type": "arc",
"start": [
362.787159,
-372.9
],
"end": [
362.787159,
-372.9
],
"center": [
362.787159,
-375.97823
],
"radius": 3.07823,
"mid": [
362.787159,
-379.05646
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 8,
"segments": [
{
"type": "arc",
"start": [
250.707159,
-372.72823
],
"end": [
250.707159,
-372.72823
],
"center": [
250.707159,
-375.97823
],
"radius": 3.25,
"mid": [
250.707159,
-379.22823
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 9,
"segments": [
{
"type": "arc",
"start": [
291.457159,
-311.1
],
"end": [
291.457159,
-311.1
],
"center": [
291.457159,
-314.17823
],
"radius": 3.07823,
"mid": [
291.457159,
-317.25646
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 10,
"segments": [
{
"type": "arc",
"start": [
44.987159,
-68.0
],
"end": [
44.987159,
-68.0
],
"center": [
44.987159,
-71.07823
],
"radius": 3.07823,
"mid": [
44.987159,
-74.15646
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 11,
"segments": [
{
"type": "arc",
"start": [
194.447159,
-372.72823
],
"end": [
194.447159,
-372.72823
],
"center": [
194.447159,
-375.97823
],
"radius": 3.25,
"mid": [
194.447159,
-379.22823
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 12,
"segments": [
{
"type": "arc",
"start": [
291.457159,
-372.9
],
"end": [
291.457159,
-372.9
],
"center": [
291.457159,
-375.97823
],
"radius": 3.07823,
"mid": [
291.457159,
-379.05646
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 13,
"segments": [
{
"type": "arc",
"start": [
125.187159,
-154.47823
],
"end": [
125.187159,
-154.47823
],
"center": [
125.187159,
-158.57823
],
"radius": 4.1,
"mid": [
125.187159,
-162.67823
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 14,
"segments": [
{
"type": "arc",
"start": [
125.187159,
-66.97823
],
"end": [
125.187159,
-66.97823
],
"center": [
125.187159,
-71.07823
],
"radius": 4.1,
"mid": [
125.187159,
-75.17823
],
"clockwise": false
}
],
"num_segments": 1
},
{
"index": 15,
"segments": [
{
"type": "arc",
"start": [
194.447159,
-272.32823
],
"end": [
194.447159,
-272.32823
],
"center": [
194.447159,
-275.57823
],
"radius": 3.25,
"mid": [
194.447159,
-278.82823
],
"clockwise": false
}
],
"num_segments": 1
}
],
"num_inner_boundaries": 16,
"thickness": null,
"transform": {
"origin": [
197.57823,
184.187159,
6.35
],
"x_axis": [
0.0,
-1.0,
0.0
],
"y_axis": [
1.0,
0.0,
-0.0
],
"normal": [
0.0,
0.0,
1.0
]
}
}

View File

@@ -0,0 +1,314 @@
{
"schema_version": "2.0",
"units": "mm",
"sandbox_id": "sandbox_2",
"outer_boundary": [
{
"type": "arc",
"start": [
0.0,
0.0
],
"end": [
7.5,
-7.5
],
"center": [
0.0,
-7.5
],
"radius": 7.5,
"mid": [
5.303301,
-2.196699
],
"clockwise": true
},
{
"type": "line",
"start": [
7.5,
-7.5
],
"end": [
7.5,
-22.6
]
},
{
"type": "line",
"start": [
7.5,
-22.6
],
"end": [
22.5,
-22.6
]
},
{
"type": "line",
"start": [
22.5,
-22.6
],
"end": [
22.5,
-13.496098
]
},
{
"type": "line",
"start": [
22.5,
-13.496098
],
"end": [
74.5,
-13.496098
]
},
{
"type": "line",
"start": [
74.5,
-13.496098
],
"end": [
74.5,
-22.6
]
},
{
"type": "line",
"start": [
74.5,
-22.6
],
"end": [
102.5,
-22.6
]
},
{
"type": "line",
"start": [
102.5,
-22.6
],
"end": [
102.5,
-7.5
]
},
{
"type": "arc",
"start": [
102.5,
-7.5
],
"end": [
117.5,
-7.5
],
"center": [
110.0,
-7.5
],
"radius": 7.5,
"mid": [
110.0,
0.0
],
"clockwise": false
},
{
"type": "line",
"start": [
117.5,
-7.5
],
"end": [
117.5,
-22.6
]
},
{
"type": "line",
"start": [
117.5,
-22.6
],
"end": [
140.748693,
-22.6
]
},
{
"type": "line",
"start": [
140.748693,
-22.6
],
"end": [
140.748693,
124.4
]
},
{
"type": "line",
"start": [
140.748693,
124.4
],
"end": [
117.5,
124.4
]
},
{
"type": "line",
"start": [
117.5,
124.4
],
"end": [
117.5,
102.5
]
},
{
"type": "arc",
"start": [
117.5,
102.5
],
"end": [
102.5,
102.5
],
"center": [
110.0,
102.5
],
"radius": 7.5,
"mid": [
110.0,
95.0
],
"clockwise": true
},
{
"type": "line",
"start": [
102.5,
102.5
],
"end": [
102.5,
124.4
]
},
{
"type": "line",
"start": [
102.5,
124.4
],
"end": [
7.5,
124.4
]
},
{
"type": "line",
"start": [
7.5,
124.4
],
"end": [
7.5,
102.5
]
},
{
"type": "arc",
"start": [
7.5,
102.5
],
"end": [
0.0,
95.0
],
"center": [
0.0,
102.5
],
"radius": 7.5,
"mid": [
5.303301,
97.196699
],
"clockwise": true
},
{
"type": "line",
"start": [
0.0,
95.0
],
"end": [
-13.5,
95.0
]
},
{
"type": "line",
"start": [
-13.5,
95.0
],
"end": [
-13.5,
0.0
]
},
{
"type": "line",
"start": [
-13.5,
0.0
],
"end": [
0.0,
0.0
]
}
],
"inner_boundaries": [],
"num_inner_boundaries": 0,
"thickness": null,
"transform": {
"origin": [
-196.0,
175.5,
4.35
],
"x_axis": [
0.0,
-1.0,
0.0
],
"y_axis": [
1.0,
0.0,
-0.0
],
"normal": [
0.0,
0.0,
1.0
]
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff