From f4cfc9b1b77e1003ede46170faefe0c71a0dde17 Mon Sep 17 00:00:00 2001 From: Antoine Date: Mon, 16 Feb 2026 18:45:24 +0000 Subject: [PATCH] feat(adaptive-isogrid): import_profile.py - push rib profile as NX sketch, sandbox1 brain input test file --- .../adaptive-isogrid/src/nx/import_profile.py | 495 +++-- .../tests/sandbox1_brain_input.json | 1925 +++++++++++++++++ 2 files changed, 2289 insertions(+), 131 deletions(-) create mode 100644 tools/adaptive-isogrid/tests/sandbox1_brain_input.json diff --git a/tools/adaptive-isogrid/src/nx/import_profile.py b/tools/adaptive-isogrid/src/nx/import_profile.py index 65cc5c18..f925b206 100644 --- a/tools/adaptive-isogrid/src/nx/import_profile.py +++ b/tools/adaptive-isogrid/src/nx/import_profile.py @@ -1,163 +1,396 @@ """ -NXOpen script — import rib profile JSON and replace sandbox geometry. +NXOpen script — Import rib profile into NX as a sketch. -Input: - rib_profile_.json (or rib_profile.json) +Reads `rib_profile_.json` (output from Python Brain) and creates +an NX sketch on the sandbox plane containing: + - Outer boundary polyline + - All pocket cutout polylines -Responsibilities: -- Recreate closed polylines from profile coordinate arrays -- Build sheet region for sandbox -- Replace sandbox face geometry only -- Sew/unite with neighboring reserved faces +The sketch is placed in the idealized part. Antoine extrudes manually the first +time; subsequent iterations only update the sketch and the extrude regenerates. + +Usage (NX Journal): + File > Execute > NX Journal > import_profile.py + +Expects rib_profile JSON files in the same `adaptive_isogrid_data/` folder +created by extract_sandbox.py (next to the idealized part). + +Author: Atomizer / Adaptive Isogrid +Created: 2026-02-16 """ from __future__ import annotations -import argparse import json -from pathlib import Path -from typing import Any, Dict, Iterable, List, Sequence, Tuple +import math +import os +from typing import Any, Dict, List, Tuple + +# --------------------------------------------------------------------------- +# Config +# --------------------------------------------------------------------------- + +SKETCH_NAME_PREFIX = "ISOGRID_RIB_" # e.g., ISOGRID_RIB_sandbox_1 -Point2D = Tuple[float, float] -Point3D = Tuple[float, float, float] +# --------------------------------------------------------------------------- +# Geometry helpers +# --------------------------------------------------------------------------- + +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 _add(a: Sequence[float], b: Sequence[float]) -> Point3D: - return (a[0] + b[0], a[1] + b[1], a[2] + b[2]) +# --------------------------------------------------------------------------- +# NX sketch creation +# --------------------------------------------------------------------------- - -def _mul(v: Sequence[float], s: float) -> Point3D: - return (v[0] * s, v[1] * s, v[2] * s) - - -def load_json(path: Path) -> Dict[str, Any]: - return json.loads(path.read_text()) - - -def map_2d_to_3d(p: Point2D, transform: Dict[str, List[float]]) -> Point3D: - origin = transform["origin"] - x_axis = transform["x_axis"] - y_axis = transform["y_axis"] - return _add(_add(origin, _mul(x_axis, p[0])), _mul(y_axis, p[1])) - - -def _ensure_closed(coords: List[Point2D]) -> List[Point2D]: - if not coords: - return coords - if coords[0] != coords[-1]: - coords.append(coords[0]) - return coords - - -def _create_polyline_curve(work_part: Any, pts3d: List[Point3D]) -> Any: +def _find_or_create_sketch( + part: Any, + sketch_name: str, + transform: Dict[str, List[float]], + lister: Any, +) -> Any: """ - Create a closed polyline curve in NX. - API notes: this can be implemented with StudioSplineBuilderEx, PolygonBuilder, - or line segments + composite curve depending on NX version/license. + Find existing sketch by name, or create a new one on the sandbox plane. + If found, delete all existing geometry in it (for update). + Returns the Sketch object. """ - # Line-segment fallback (works in all NX versions) - curves = [] - for i in range(len(pts3d) - 1): - p1 = work_part.Points.CreatePoint(pts3d[i]) - p2 = work_part.Points.CreatePoint(pts3d[i + 1]) - curves.append(work_part.Curves.CreateLine(p1, p2)) - return curves + import NXOpen - -def build_profile_curves(work_part: Any, profile: Dict[str, Any], transform: Dict[str, List[float]]) -> Dict[str, List[Any]]: - created: Dict[str, List[Any]] = {"outer": [], "pockets": [], "holes": []} - - outer = _ensure_closed([(float(x), float(y)) for x, y in profile["outer_boundary"]]) - outer_3d = [map_2d_to_3d(p, transform) for p in outer] - created["outer"] = _create_polyline_curve(work_part, outer_3d) - - for pocket in profile.get("pockets", []): - coords = _ensure_closed([(float(x), float(y)) for x, y in pocket]) - pts3d = [map_2d_to_3d(p, transform) for p in coords] - created["pockets"].extend(_create_polyline_curve(work_part, pts3d)) - - for hole in profile.get("hole_boundaries", []): - coords = _ensure_closed([(float(x), float(y)) for x, y in hole]) - pts3d = [map_2d_to_3d(p, transform) for p in coords] - created["holes"].extend(_create_polyline_curve(work_part, pts3d)) - - return created - - -def _find_sandbox_face(work_part: Any, sandbox_id: str) -> Any: - for body in getattr(work_part.Bodies, "ToArray", lambda: work_part.Bodies)(): - for face in body.GetFaces(): + # Try to find existing sketch by name + existing_sketch = None + try: + for feat in part.Features: + fname = "" try: - tag = face.GetStringUserAttribute("ISOGRID_SANDBOX", -1) + fname = feat.Name except Exception: - tag = None - if tag == sandbox_id: - return face - raise RuntimeError(f"Sandbox face not found for id={sandbox_id}") + 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}") + except Exception: + pass + break + except Exception: + pass + if existing_sketch is not None: + # Clear existing geometry for update + try: + existing_sketch.Activate(NXOpen.Sketch.ViewReorient.DoNotOrientView) + all_geom = existing_sketch.GetAllGeometry() + if all_geom: + existing_sketch.DeleteObjects(list(all_geom)) + lister.WriteLine(f"[import] Cleared {len(all_geom)} objects from existing sketch") + existing_sketch.Deactivate( + NXOpen.Sketch.ViewReorient.DoNotOrientView, + NXOpen.Sketch.UpdateLevel.Model, + ) + except Exception as exc: + lister.WriteLine(f"[import] Warning clearing sketch: {exc}") + return existing_sketch -def replace_sandbox_face_geometry(work_part: Any, sandbox_face: Any, created_curves: Dict[str, List[Any]]) -> None: - """ - Replace sandbox surface region from generated profile curves. + # Create new sketch on the sandbox plane + lister.WriteLine(f"[import] Creating new sketch: {sketch_name}") - This operation depends on the model topology and NX license package. - Typical implementation: - 1) Build bounded plane/sheet from outer and inner loops - 2) Trim/split host face by new boundaries - 3) Delete old sandbox patch - 4) Sew new patch with reserved neighboring faces - 5) Unite if multiple sheet bodies are produced - """ - # Recommended implementation hook points. - # - Through Curve Mesh / Bounded Plane builders in NXOpen.Features - # - SewBuilder in NXOpen.Features - # - DeleteFace + ReplaceFace in synchronous modeling toolkit - raise NotImplementedError( - "Sandbox face replacement is model-specific. Implement with NXOpen feature builders " - "(bounded sheet + replace face + sew/unite) in target NX environment." + origin = transform["origin"] + normal = transform["normal"] + x_axis = transform["x_axis"] + + # Create datum plane at the sandbox location + sketch_builder = part.Sketches.CreateNewSketchInPlaceBuilder(NXOpen.Sketch.Null) + + # Set the plane + origin_pt = NXOpen.Point3d(origin[0], origin[1], origin[2]) + 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 datum plane for the sketch + plane = part.Datums.CreateFixedDatumPlane(origin_pt, normal_vec) + sketch_builder.PlaneReference(plane) + + # Set sketch origin + sketch_builder.SketchOrigin(origin_pt) + sketch_builder.AxisReference( + part.Datums.CreateFixedDatumAxis(origin_pt, x_vec) ) + # Commit + sketch_feature = sketch_builder.CommitFeature() + sketch_builder.Destroy() -def run_in_nx( - profile_path: Path, - geometry_path: Path, - sandbox_id: str, -) -> None: - import NXOpen # type: ignore + # Get the sketch object + sketch = sketch_feature.GetEntities()[0] + + # Rename the feature + try: + sketch_feature.Name = sketch_name + except Exception: + pass + + lister.WriteLine(f"[import] Created sketch: {sketch_name}") + return sketch + + +def _draw_polyline_in_sketch( + part: Any, + sketch: Any, + points_3d: List[Tuple[float, float, float]], + lister: Any, + close: bool = True, +) -> int: + """ + Draw a closed polyline in the sketch using individual line segments. + Returns number of lines created. + """ + import NXOpen + + if len(points_3d) < 2: + return 0 + + lines_created = 0 + n = len(points_3d) + + # If last point == first point, don't double-close + if close and len(points_3d) >= 3: + d = math.sqrt(sum((a - b) ** 2 for a, b in zip(points_3d[0], points_3d[-1]))) + if d < 0.001: + n = len(points_3d) - 1 # skip duplicate closing point + + segments = n if close else (n - 1) + + for i in range(segments): + p1 = points_3d[i] + p2 = points_3d[(i + 1) % n] + + try: + start_pt = NXOpen.Point3d(p1[0], p1[1], p1[2]) + end_pt = NXOpen.Point3d(p2[0], p2[1], p2[2]) + + line_builder = part.Sketches.CreateLineBuilder() + line_builder.SetStartPoint(start_pt) + line_builder.SetEndPoint(end_pt) + line = line_builder.Commit() + line_builder.Destroy() + + lines_created += 1 + except Exception: + # Fallback: try creating a curve and adding to sketch + try: + start_obj = part.Points.CreatePoint(NXOpen.Point3d(p1[0], p1[1], p1[2])) + end_obj = part.Points.CreatePoint(NXOpen.Point3d(p2[0], p2[1], p2[2])) + line = part.Curves.CreateLine(start_obj, end_obj) + sketch.AddGeometry(line, NXOpen.Sketch.InferConstraintsOption.DoNotInferConstraints) + lines_created += 1 + except Exception as exc2: + if lines_created == 0: + lister.WriteLine(f"[import] Line creation failed: {exc2}") + + return lines_created + + +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.""" + import NXOpen + + 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 + + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- + +def main(): + import NXOpen session = NXOpen.Session.GetSession() + lister = session.ListingWindow + lister.Open() + + lister.WriteLine("=" * 60) + lister.WriteLine(" Adaptive Isogrid — Rib Profile Import") + lister.WriteLine("=" * 60) + + # Navigate to idealized part work_part = session.Parts.Work - if work_part is None: - raise RuntimeError("No active NX work part.") + part_name = work_part.Name if hasattr(work_part, "Name") else "" + lister.WriteLine(f"[import] Work part: {part_name}") - profile = load_json(profile_path) - geometry = load_json(geometry_path) - transform = geometry.get("transform") - if not transform: - raise ValueError(f"Missing transform in {geometry_path}") + # 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 "" + if pname.endswith("_i"): + session.Parts.SetWork(part) + work_part = part + lister.WriteLine(f"[import] Switched to idealized part: {pname}") + break - sandbox_face = _find_sandbox_face(work_part, sandbox_id) - created_curves = build_profile_curves(work_part, profile, transform) - replace_sandbox_face_geometry(work_part, sandbox_face, created_curves) + # Find data directory + try: + part_dir = os.path.dirname(work_part.FullPath) + except Exception: + part_dir = os.getcwd() - print(f"[import_profile] Imported profile for {sandbox_id}: {profile_path}") + data_dir = os.path.join(part_dir, "adaptive_isogrid_data") + + if not os.path.isdir(data_dir): + lister.WriteLine(f"[import] ERROR: Data directory not found: {data_dir}") + return + + # Find all rib profile + geometry JSON pairs + 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)}") + return + + lister.WriteLine(f"[import] Found {len(profile_files)} profile(s) to import") + + 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 + + # 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") + continue + + lister.WriteLine(f"\n--- Importing {profile_file} ---") + lister.WriteLine(f"[import] 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"[import] ERROR reading JSON: {exc}") + continue + + transform = geometry["transform"] + sketch_name = SKETCH_NAME_PREFIX + sandbox_id + + # Find or create sketch + try: + sketch = _find_or_create_sketch(work_part, sketch_name, transform, lister) + except Exception as exc: + lister.WriteLine(f"[import] ERROR creating sketch: {exc}") + import traceback + lister.WriteLine(traceback.format_exc()) + continue + + # Activate sketch for drawing + try: + sketch.Activate(NXOpen.Sketch.ViewReorient.DoNotOrientView) + except Exception as exc: + lister.WriteLine(f"[import] ERROR activating sketch: {exc}") + continue + + total_lines = 0 + + # Draw outer boundary + outer_2d = profile.get("outer_boundary", []) + if outer_2d: + outer_3d = unproject_to_3d(outer_2d, transform) + n = _draw_polyline_in_sketch(work_part, sketch, outer_3d, lister, close=True) + total_lines += n + lister.WriteLine(f"[import] Outer boundary: {n} lines") + + # Draw pocket cutouts + pockets = profile.get("pockets", []) + lister.WriteLine(f"[import] Drawing {len(pockets)} pockets...") + + for pi, pocket_pts in enumerate(pockets): + if len(pocket_pts) < 3: + continue + pocket_3d = unproject_to_3d(pocket_pts, transform) + n = _draw_polyline_in_sketch(work_part, sketch, pocket_3d, lister, close=True) + total_lines += n + + # Progress every 50 pockets + if (pi + 1) % 50 == 0: + lister.WriteLine(f"[import] ... {pi + 1}/{len(pockets)} pockets drawn") + + # Deactivate sketch + try: + sketch.Deactivate( + NXOpen.Sketch.ViewReorient.DoNotOrientView, + NXOpen.Sketch.UpdateLevel.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("\n" + "=" * 60) + lister.WriteLine(" Import complete — extrude the sketch to rib thickness") + lister.WriteLine("=" * 60) -def main(argv: Sequence[str] | None = None) -> int: - parser = argparse.ArgumentParser(description="Import rib profile JSON into NX sandbox face") - parser.add_argument("--profile", required=True, help="Path to rib_profile json") - parser.add_argument("--geometry", required=True, help="Path to geometry_sandbox json") - parser.add_argument("--sandbox-id", required=True, help="Sandbox id (e.g. sandbox_1)") - args = parser.parse_args(argv) - - run_in_nx( - profile_path=Path(args.profile), - geometry_path=Path(args.geometry), - sandbox_id=args.sandbox_id, - ) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) +main() diff --git a/tools/adaptive-isogrid/tests/sandbox1_brain_input.json b/tools/adaptive-isogrid/tests/sandbox1_brain_input.json new file mode 100644 index 00000000..0e684be0 --- /dev/null +++ b/tools/adaptive-isogrid/tests/sandbox1_brain_input.json @@ -0,0 +1,1925 @@ +{ + "plate_id": "sandbox_1", + "units": "mm", + "thickness": 12.7, + "outer_boundary": [ + [ + 381.787159, + 14.92177 + ], + [ + 132.687159, + 14.92177 + ], + [ + 132.687159, + -13.57823 + ], + [ + 88.687159, + -13.57823 + ], + [ + 88.687159, + 14.92177 + ], + [ + -13.412841, + 14.92177 + ], + [ + -13.412841, + 0.02177 + ], + [ + -30.812841, + 0.02177 + ], + [ + -30.812841, + -254.17823 + ], + [ + 169.435852, + -254.17823 + ], + [ + 169.435852, + -417.57823 + ], + [ + 197.121675, + -417.57823 + ], + [ + 197.121675, + -401.57823 + ], + [ + 212.121675, + -401.57823 + ], + [ + 212.121675, + -417.57823 + ], + [ + 289.687159, + -417.57823 + ], + [ + 304.687159, + -406.57823 + ], + [ + 317.687159, + -406.57823 + ], + [ + 332.687159, + -417.57823 + ], + [ + 381.787159, + -417.57823 + ], + [ + 381.787159, + -395.17823 + ], + [ + 404.187159, + -395.17823 + ], + [ + 404.187159, + -322.57823 + ], + [ + 352.787159, + -322.57823 + ], + [ + 352.787159, + -304.17823 + ], + [ + 361.187159, + -304.17823 + ], + [ + 361.187159, + -24.57823 + ], + [ + 404.187159, + -24.57823 + ], + [ + 404.187159, + 0.02177 + ], + [ + 381.787159, + 0.02177 + ], + [ + 381.787159, + 14.92177 + ] + ], + "holes": [ + { + "index": 0, + "center": [ + 0.0, + 0.1231292 + ], + "diameter": 5.9102016, + "is_circular": true, + "boundary": [ + [ + 0.0, + 3.07823 + ], + [ + -0.796705, + 2.973342 + ], + [ + -1.539115, + 2.665825 + ], + [ + -2.176637, + 2.176637 + ], + [ + -2.665825, + 1.539115 + ], + [ + -2.973342, + 0.796705 + ], + [ + -3.07823, + 0.0 + ], + [ + -2.973342, + -0.796705 + ], + [ + -2.665825, + -1.539115 + ], + [ + -2.176637, + -2.176637 + ], + [ + -1.539115, + -2.665825 + ], + [ + -0.796705, + -2.973342 + ], + [ + 0.0, + -3.07823 + ], + [ + 0.796705, + -2.973342 + ], + [ + 1.539115, + -2.665825 + ], + [ + 2.176637, + -2.176637 + ], + [ + 2.665825, + -1.539115 + ], + [ + 2.973342, + -0.796705 + ], + [ + 3.07823, + 0.0 + ], + [ + 2.973342, + 0.796705 + ], + [ + 2.665825, + 1.539115 + ], + [ + 2.176637, + 2.176637 + ], + [ + 1.539115, + 2.665825 + ], + [ + 0.796705, + 2.973342 + ], + [ + 0.0, + 3.07823 + ] + ], + "weight": 0.5 + }, + { + "index": 1, + "center": [ + 366.187159, + 1.1857699999999998 + ], + "diameter": 7.872, + "is_circular": true, + "boundary": [ + [ + 366.187159, + 5.12177 + ], + [ + 365.126001, + 4.982066 + ], + [ + 364.137159, + 4.572474 + ], + [ + 363.288021, + 3.920908 + ], + [ + 362.636455, + 3.07177 + ], + [ + 362.226863, + 2.082928 + ], + [ + 362.087159, + 1.02177 + ], + [ + 362.226863, + -0.039388 + ], + [ + 362.636455, + -1.02823 + ], + [ + 363.288021, + -1.877368 + ], + [ + 364.137159, + -2.528934 + ], + [ + 365.126001, + -2.938526 + ], + [ + 366.187159, + -3.07823 + ], + [ + 367.248317, + -2.938526 + ], + [ + 368.237159, + -2.528934 + ], + [ + 369.086297, + -1.877368 + ], + [ + 369.737863, + -1.02823 + ], + [ + 370.147455, + -0.039388 + ], + [ + 370.287159, + 1.02177 + ], + [ + 370.147455, + 2.082928 + ], + [ + 369.737863, + 3.07177 + ], + [ + 369.086297, + 3.920908 + ], + [ + 368.237159, + 4.572474 + ], + [ + 367.248317, + 4.982066 + ], + [ + 366.187159, + 5.12177 + ] + ], + "weight": 0.5 + }, + { + "index": 2, + "center": [ + 44.98715876, + 0.1231292 + ], + "diameter": 5.910201600000019, + "is_circular": true, + "boundary": [ + [ + 44.987159, + 3.07823 + ], + [ + 44.190454, + 2.973342 + ], + [ + 43.448044, + 2.665825 + ], + [ + 42.810521, + 2.176637 + ], + [ + 42.321333, + 1.539115 + ], + [ + 42.013817, + 0.796705 + ], + [ + 41.908929, + 0.0 + ], + [ + 42.013817, + -0.796705 + ], + [ + 42.321333, + -1.539115 + ], + [ + 42.810521, + -2.176637 + ], + [ + 43.448044, + -2.665825 + ], + [ + 44.190454, + -2.973342 + ], + [ + 44.987159, + -3.07823 + ], + [ + 45.783863, + -2.973342 + ], + [ + 46.526274, + -2.665825 + ], + [ + 47.163796, + -2.176637 + ], + [ + 47.652984, + -1.539115 + ], + [ + 47.960501, + -0.796705 + ], + [ + 48.065389, + 0.0 + ], + [ + 47.960501, + 0.796705 + ], + [ + 47.652984, + 1.539115 + ], + [ + 47.163796, + 2.176637 + ], + [ + 46.526274, + 2.665825 + ], + [ + 45.783863, + 2.973342 + ], + [ + 44.987159, + 3.07823 + ] + ], + "weight": 0.5 + }, + { + "index": 3, + "center": [ + 250.70715892, + -272.19823 + ], + "diameter": 6.240000000000012, + "is_circular": true, + "boundary": [ + [ + 250.707159, + -269.07823 + ], + [ + 249.865997, + -269.188971 + ], + [ + 249.082159, + -269.513647 + ], + [ + 248.409062, + -270.030133 + ], + [ + 247.892576, + -270.70323 + ], + [ + 247.5679, + -271.487068 + ], + [ + 247.457159, + -272.32823 + ], + [ + 247.5679, + -273.169392 + ], + [ + 247.892576, + -273.95323 + ], + [ + 248.409062, + -274.626327 + ], + [ + 249.082159, + -275.142813 + ], + [ + 249.865997, + -275.467489 + ], + [ + 250.707159, + -275.57823 + ], + [ + 251.548321, + -275.467489 + ], + [ + 252.332159, + -275.142813 + ], + [ + 253.005256, + -274.626327 + ], + [ + 253.521741, + -273.95323 + ], + [ + 253.846418, + -273.169392 + ], + [ + 253.957159, + -272.32823 + ], + [ + 253.846418, + -271.487068 + ], + [ + 253.521741, + -270.70323 + ], + [ + 253.005256, + -270.030133 + ], + [ + 252.332159, + -269.513647 + ], + [ + 251.548321, + -269.188971 + ], + [ + 250.707159, + -269.07823 + ] + ], + "weight": 0.5 + }, + { + "index": 4, + "center": [ + 44.98715876, + -155.3768708 + ], + "diameter": 5.910201600000013, + "is_circular": true, + "boundary": [ + [ + 44.987159, + -152.42177 + ], + [ + 44.190454, + -152.526658 + ], + [ + 43.448044, + -152.834175 + ], + [ + 42.810521, + -153.323363 + ], + [ + 42.321333, + -153.960885 + ], + [ + 42.013817, + -154.703295 + ], + [ + 41.908929, + -155.5 + ], + [ + 42.013817, + -156.296705 + ], + [ + 42.321333, + -157.039115 + ], + [ + 42.810521, + -157.676637 + ], + [ + 43.448044, + -158.165825 + ], + [ + 44.190454, + -158.473342 + ], + [ + 44.987159, + -158.57823 + ], + [ + 45.783863, + -158.473342 + ], + [ + 46.526274, + -158.165825 + ], + [ + 47.163796, + -157.676637 + ], + [ + 47.652984, + -157.039115 + ], + [ + 47.960501, + -156.296705 + ], + [ + 48.065389, + -155.5 + ], + [ + 47.960501, + -154.703295 + ], + [ + 47.652984, + -153.960885 + ], + [ + 47.163796, + -153.323363 + ], + [ + 46.526274, + -152.834175 + ], + [ + 45.783863, + -152.526658 + ], + [ + 44.987159, + -152.42177 + ] + ], + "weight": 0.5 + }, + { + "index": 5, + "center": [ + 125.187159, + -232.31422999999998 + ], + "diameter": 7.871999999999957, + "is_circular": true, + "boundary": [ + [ + 125.187159, + -228.37823 + ], + [ + 124.126001, + -228.517934 + ], + [ + 123.137159, + -228.927526 + ], + [ + 122.288021, + -229.579092 + ], + [ + 121.636455, + -230.42823 + ], + [ + 121.226863, + -231.417072 + ], + [ + 121.087159, + -232.47823 + ], + [ + 121.226863, + -233.539388 + ], + [ + 121.636455, + -234.52823 + ], + [ + 122.288021, + -235.377368 + ], + [ + 123.137159, + -236.028934 + ], + [ + 124.126001, + -236.438526 + ], + [ + 125.187159, + -236.57823 + ], + [ + 126.248317, + -236.438526 + ], + [ + 127.237159, + -236.028934 + ], + [ + 128.086297, + -235.377368 + ], + [ + 128.737863, + -234.52823 + ], + [ + 129.147455, + -233.539388 + ], + [ + 129.287159, + -232.47823 + ], + [ + 129.147455, + -231.417072 + ], + [ + 128.737863, + -230.42823 + ], + [ + 128.086297, + -229.579092 + ], + [ + 127.237159, + -228.927526 + ], + [ + 126.248317, + -228.517934 + ], + [ + 125.187159, + -228.37823 + ] + ], + "weight": 0.5 + }, + { + "index": 6, + "center": [ + -9.81284108, + -67.69823000000001 + ], + "diameter": 6.240000000000012, + "is_circular": true, + "boundary": [ + [ + -9.812841, + -64.57823 + ], + [ + -10.654003, + -64.688971 + ], + [ + -11.437841, + -65.013647 + ], + [ + -12.110938, + -65.530133 + ], + [ + -12.627424, + -66.20323 + ], + [ + -12.9521, + -66.987068 + ], + [ + -13.062841, + -67.82823 + ], + [ + -12.9521, + -68.669392 + ], + [ + -12.627424, + -69.45323 + ], + [ + -12.110938, + -70.126327 + ], + [ + -11.437841, + -70.642813 + ], + [ + -10.654003, + -70.967489 + ], + [ + -9.812841, + -71.07823 + ], + [ + -8.971679, + -70.967489 + ], + [ + -8.187841, + -70.642813 + ], + [ + -7.514744, + -70.126327 + ], + [ + -6.998259, + -69.45323 + ], + [ + -6.673582, + -68.669392 + ], + [ + -6.562841, + -67.82823 + ], + [ + -6.673582, + -66.987068 + ], + [ + -6.998259, + -66.20323 + ], + [ + -7.514744, + -65.530133 + ], + [ + -8.187841, + -65.013647 + ], + [ + -8.971679, + -64.688971 + ], + [ + -9.812841, + -64.57823 + ] + ], + "weight": 0.5 + }, + { + "index": 7, + "center": [ + 362.78715876, + -372.77687080000004 + ], + "diameter": 5.910201600000069, + "is_circular": true, + "boundary": [ + [ + 362.787159, + -369.82177 + ], + [ + 361.990454, + -369.926658 + ], + [ + 361.248044, + -370.234175 + ], + [ + 360.610521, + -370.723363 + ], + [ + 360.121333, + -371.360885 + ], + [ + 359.813817, + -372.103295 + ], + [ + 359.708929, + -372.9 + ], + [ + 359.813817, + -373.696705 + ], + [ + 360.121333, + -374.439115 + ], + [ + 360.610521, + -375.076637 + ], + [ + 361.248044, + -375.565825 + ], + [ + 361.990454, + -375.873342 + ], + [ + 362.787159, + -375.97823 + ], + [ + 363.583863, + -375.873342 + ], + [ + 364.326274, + -375.565825 + ], + [ + 364.963796, + -375.076637 + ], + [ + 365.452984, + -374.439115 + ], + [ + 365.760501, + -373.696705 + ], + [ + 365.865389, + -372.9 + ], + [ + 365.760501, + -372.103295 + ], + [ + 365.452984, + -371.360885 + ], + [ + 364.963796, + -370.723363 + ], + [ + 364.326274, + -370.234175 + ], + [ + 363.583863, + -369.926658 + ], + [ + 362.787159, + -369.82177 + ] + ], + "weight": 0.5 + }, + { + "index": 8, + "center": [ + 250.70715892, + -372.59822999999994 + ], + "diameter": 6.239999999999898, + "is_circular": true, + "boundary": [ + [ + 250.707159, + -369.47823 + ], + [ + 249.865997, + -369.588971 + ], + [ + 249.082159, + -369.913647 + ], + [ + 248.409062, + -370.430133 + ], + [ + 247.892576, + -371.10323 + ], + [ + 247.5679, + -371.887068 + ], + [ + 247.457159, + -372.72823 + ], + [ + 247.5679, + -373.569392 + ], + [ + 247.892576, + -374.35323 + ], + [ + 248.409062, + -375.026327 + ], + [ + 249.082159, + -375.542813 + ], + [ + 249.865997, + -375.867489 + ], + [ + 250.707159, + -375.97823 + ], + [ + 251.548321, + -375.867489 + ], + [ + 252.332159, + -375.542813 + ], + [ + 253.005256, + -375.026327 + ], + [ + 253.521741, + -374.35323 + ], + [ + 253.846418, + -373.569392 + ], + [ + 253.957159, + -372.72823 + ], + [ + 253.846418, + -371.887068 + ], + [ + 253.521741, + -371.10323 + ], + [ + 253.005256, + -370.430133 + ], + [ + 252.332159, + -369.913647 + ], + [ + 251.548321, + -369.588971 + ], + [ + 250.707159, + -369.47823 + ] + ], + "weight": 0.5 + }, + { + "index": 9, + "center": [ + 291.45715876, + -310.9768708 + ], + "diameter": 5.910201599999956, + "is_circular": true, + "boundary": [ + [ + 291.457159, + -308.02177 + ], + [ + 290.660454, + -308.126658 + ], + [ + 289.918044, + -308.434175 + ], + [ + 289.280521, + -308.923363 + ], + [ + 288.791333, + -309.560885 + ], + [ + 288.483817, + -310.303295 + ], + [ + 288.378929, + -311.1 + ], + [ + 288.483817, + -311.896705 + ], + [ + 288.791333, + -312.639115 + ], + [ + 289.280521, + -313.276637 + ], + [ + 289.918044, + -313.765825 + ], + [ + 290.660454, + -314.073342 + ], + [ + 291.457159, + -314.17823 + ], + [ + 292.253863, + -314.073342 + ], + [ + 292.996274, + -313.765825 + ], + [ + 293.633796, + -313.276637 + ], + [ + 294.122984, + -312.639115 + ], + [ + 294.430501, + -311.896705 + ], + [ + 294.535389, + -311.1 + ], + [ + 294.430501, + -310.303295 + ], + [ + 294.122984, + -309.560885 + ], + [ + 293.633796, + -308.923363 + ], + [ + 292.996274, + -308.434175 + ], + [ + 292.253863, + -308.126658 + ], + [ + 291.457159, + -308.02177 + ] + ], + "weight": 0.5 + }, + { + "index": 10, + "center": [ + 44.98715876, + -67.87687079999999 + ], + "diameter": 5.910201600000013, + "is_circular": true, + "boundary": [ + [ + 44.987159, + -64.92177 + ], + [ + 44.190454, + -65.026658 + ], + [ + 43.448044, + -65.334175 + ], + [ + 42.810521, + -65.823363 + ], + [ + 42.321333, + -66.460885 + ], + [ + 42.013817, + -67.203295 + ], + [ + 41.908929, + -68.0 + ], + [ + 42.013817, + -68.796705 + ], + [ + 42.321333, + -69.539115 + ], + [ + 42.810521, + -70.176637 + ], + [ + 43.448044, + -70.665825 + ], + [ + 44.190454, + -70.973342 + ], + [ + 44.987159, + -71.07823 + ], + [ + 45.783863, + -70.973342 + ], + [ + 46.526274, + -70.665825 + ], + [ + 47.163796, + -70.176637 + ], + [ + 47.652984, + -69.539115 + ], + [ + 47.960501, + -68.796705 + ], + [ + 48.065389, + -68.0 + ], + [ + 47.960501, + -67.203295 + ], + [ + 47.652984, + -66.460885 + ], + [ + 47.163796, + -65.823363 + ], + [ + 46.526274, + -65.334175 + ], + [ + 45.783863, + -65.026658 + ], + [ + 44.987159, + -64.92177 + ] + ], + "weight": 0.5 + }, + { + "index": 11, + "center": [ + 194.44715892, + -372.59822999999994 + ], + "diameter": 6.239999999999898, + "is_circular": true, + "boundary": [ + [ + 194.447159, + -369.47823 + ], + [ + 193.605997, + -369.588971 + ], + [ + 192.822159, + -369.913647 + ], + [ + 192.149062, + -370.430133 + ], + [ + 191.632576, + -371.10323 + ], + [ + 191.3079, + -371.887068 + ], + [ + 191.197159, + -372.72823 + ], + [ + 191.3079, + -373.569392 + ], + [ + 191.632576, + -374.35323 + ], + [ + 192.149062, + -375.026327 + ], + [ + 192.822159, + -375.542813 + ], + [ + 193.605997, + -375.867489 + ], + [ + 194.447159, + -375.97823 + ], + [ + 195.288321, + -375.867489 + ], + [ + 196.072159, + -375.542813 + ], + [ + 196.745256, + -375.026327 + ], + [ + 197.261741, + -374.35323 + ], + [ + 197.586418, + -373.569392 + ], + [ + 197.697159, + -372.72823 + ], + [ + 197.586418, + -371.887068 + ], + [ + 197.261741, + -371.10323 + ], + [ + 196.745256, + -370.430133 + ], + [ + 196.072159, + -369.913647 + ], + [ + 195.288321, + -369.588971 + ], + [ + 194.447159, + -369.47823 + ] + ], + "weight": 0.5 + }, + { + "index": 12, + "center": [ + 291.45715876, + -372.77687080000004 + ], + "diameter": 5.910201600000069, + "is_circular": true, + "boundary": [ + [ + 291.457159, + -369.82177 + ], + [ + 290.660454, + -369.926658 + ], + [ + 289.918044, + -370.234175 + ], + [ + 289.280521, + -370.723363 + ], + [ + 288.791333, + -371.360885 + ], + [ + 288.483817, + -372.103295 + ], + [ + 288.378929, + -372.9 + ], + [ + 288.483817, + -373.696705 + ], + [ + 288.791333, + -374.439115 + ], + [ + 289.280521, + -375.076637 + ], + [ + 289.918044, + -375.565825 + ], + [ + 290.660454, + -375.873342 + ], + [ + 291.457159, + -375.97823 + ], + [ + 292.253863, + -375.873342 + ], + [ + 292.996274, + -375.565825 + ], + [ + 293.633796, + -375.076637 + ], + [ + 294.122984, + -374.439115 + ], + [ + 294.430501, + -373.696705 + ], + [ + 294.535389, + -372.9 + ], + [ + 294.430501, + -372.103295 + ], + [ + 294.122984, + -371.360885 + ], + [ + 293.633796, + -370.723363 + ], + [ + 292.996274, + -370.234175 + ], + [ + 292.253863, + -369.926658 + ], + [ + 291.457159, + -369.82177 + ] + ], + "weight": 0.5 + }, + { + "index": 13, + "center": [ + 125.187159, + -154.31423 + ], + "diameter": 7.872000000000014, + "is_circular": true, + "boundary": [ + [ + 125.187159, + -150.37823 + ], + [ + 124.126001, + -150.517934 + ], + [ + 123.137159, + -150.927526 + ], + [ + 122.288021, + -151.579092 + ], + [ + 121.636455, + -152.42823 + ], + [ + 121.226863, + -153.417072 + ], + [ + 121.087159, + -154.47823 + ], + [ + 121.226863, + -155.539388 + ], + [ + 121.636455, + -156.52823 + ], + [ + 122.288021, + -157.377368 + ], + [ + 123.137159, + -158.028934 + ], + [ + 124.126001, + -158.438526 + ], + [ + 125.187159, + -158.57823 + ], + [ + 126.248317, + -158.438526 + ], + [ + 127.237159, + -158.028934 + ], + [ + 128.086297, + -157.377368 + ], + [ + 128.737863, + -156.52823 + ], + [ + 129.147455, + -155.539388 + ], + [ + 129.287159, + -154.47823 + ], + [ + 129.147455, + -153.417072 + ], + [ + 128.737863, + -152.42823 + ], + [ + 128.086297, + -151.579092 + ], + [ + 127.237159, + -150.927526 + ], + [ + 126.248317, + -150.517934 + ], + [ + 125.187159, + -150.37823 + ] + ], + "weight": 0.5 + }, + { + "index": 14, + "center": [ + 125.187159, + -66.81423 + ], + "diameter": 7.871999999999986, + "is_circular": true, + "boundary": [ + [ + 125.187159, + -62.87823 + ], + [ + 124.126001, + -63.017934 + ], + [ + 123.137159, + -63.427526 + ], + [ + 122.288021, + -64.079092 + ], + [ + 121.636455, + -64.92823 + ], + [ + 121.226863, + -65.917072 + ], + [ + 121.087159, + -66.97823 + ], + [ + 121.226863, + -68.039388 + ], + [ + 121.636455, + -69.02823 + ], + [ + 122.288021, + -69.877368 + ], + [ + 123.137159, + -70.528934 + ], + [ + 124.126001, + -70.938526 + ], + [ + 125.187159, + -71.07823 + ], + [ + 126.248317, + -70.938526 + ], + [ + 127.237159, + -70.528934 + ], + [ + 128.086297, + -69.877368 + ], + [ + 128.737863, + -69.02823 + ], + [ + 129.147455, + -68.039388 + ], + [ + 129.287159, + -66.97823 + ], + [ + 129.147455, + -65.917072 + ], + [ + 128.737863, + -64.92823 + ], + [ + 128.086297, + -64.079092 + ], + [ + 127.237159, + -63.427526 + ], + [ + 126.248317, + -63.017934 + ], + [ + 125.187159, + -62.87823 + ] + ], + "weight": 0.5 + }, + { + "index": 15, + "center": [ + 194.44715892, + -272.19823 + ], + "diameter": 6.240000000000012, + "is_circular": true, + "boundary": [ + [ + 194.447159, + -269.07823 + ], + [ + 193.605997, + -269.188971 + ], + [ + 192.822159, + -269.513647 + ], + [ + 192.149062, + -270.030133 + ], + [ + 191.632576, + -270.70323 + ], + [ + 191.3079, + -271.487068 + ], + [ + 191.197159, + -272.32823 + ], + [ + 191.3079, + -273.169392 + ], + [ + 191.632576, + -273.95323 + ], + [ + 192.149062, + -274.626327 + ], + [ + 192.822159, + -275.142813 + ], + [ + 193.605997, + -275.467489 + ], + [ + 194.447159, + -275.57823 + ], + [ + 195.288321, + -275.467489 + ], + [ + 196.072159, + -275.142813 + ], + [ + 196.745256, + -274.626327 + ], + [ + 197.261741, + -273.95323 + ], + [ + 197.586418, + -273.169392 + ], + [ + 197.697159, + -272.32823 + ], + [ + 197.586418, + -271.487068 + ], + [ + 197.261741, + -270.70323 + ], + [ + 196.745256, + -270.030133 + ], + [ + 196.072159, + -269.513647 + ], + [ + 195.288321, + -269.188971 + ], + [ + 194.447159, + -269.07823 + ] + ], + "weight": 0.5 + } + ] +} \ No newline at end of file