"""Door robot arm: the obstacles round the arms, as surface points for the fit (2026-10-18, scale-up). Reads a .blend (the ship, read only, or this model's file) and samples the surfaces of everything the depsgraph shows (collection instances and linked library objects included) inside the pockets' and the moon pool's box under the deck top, except: this model's own parts ('DoorArm...', the installed arms and their rail system), the user's old rail boxes and carriage placeholders (hidden in the ship), and the two moon pool doors (the fit moves them with their animation). Writes build/obstacles_.npz: points (float32, metres, ship coordinates), their object index, the object names. blender -b --factory-startup --python build/dra_obstacles.py -- [scene name] """ import os import sys import math import random import numpy as np import bpy from mathutils import Vector HERE = os.path.dirname(os.path.abspath(__file__)) args = sys.argv[sys.argv.index("--") + 1:] path, tag = args[0], args[1] bpy.ops.wm.open_mainfile(filepath=path, load_ui=False) if len(args) > 2: sc = bpy.data.scenes[args[2]] if bpy.context.window_manager.windows: bpy.context.window_manager.windows[0].scene = sc sc = bpy.context.scene sc.frame_set(1) dg = bpy.context.evaluated_depsgraph_get() REG = ((-10.2, 16.2), (-9.7, 2.2), (-11.2, -3.30)) STEP = 0.04 SKIP = ("DoorArm", "Moonpool_Door_Aft", "Moonpool_Door_Fore", "upper rail", "rail carriage") rng = random.Random(3) pts, ids, names = [], [], [] for inst in dg.object_instances: ob = inst.object if ob.type not in ('MESH', 'CURVE', 'SURFACE', 'FONT', 'META'): continue nm = ob.name if any(s in nm for s in SKIP): continue M = inst.matrix_world.copy() bb = [M @ Vector(c) for c in ob.bound_box] if any(max(p[i] for p in bb) < REG[i][0] or min(p[i] for p in bb) > REG[i][1] for i in range(3)): continue try: me = ob.to_mesh() except Exception: continue if me is None: continue me.calc_loop_triangles() V = [M @ v.co for v in me.vertices] k = len(names) names.append(nm + ((" <" + inst.instance_object.name + ">") if inst.is_instance and inst.instance_object else "")) n0 = len(pts) for lt in me.loop_triangles: a, b, c = (V[i] for i in lt.vertices) lo = [min(a[i], b[i], c[i]) for i in range(3)] hi = [max(a[i], b[i], c[i]) for i in range(3)] if any(hi[i] < REG[i][0] or lo[i] > REG[i][1] for i in range(3)): continue area = (b - a).cross(c - a).length / 2 n = max(1, int(math.ceil(area / (STEP * STEP)))) for v in (a, b, c): pts.append((v.x, v.y, v.z)) ids.append(k) for _ in range(n): r1, r2 = rng.random(), rng.random() if r1 + r2 > 1: r1, r2 = 1 - r1, 1 - r2 p = a + (b - a) * r1 + (c - a) * r2 pts.append((p.x, p.y, p.z)) ids.append(k) ob.to_mesh_clear() print("[ob] %-60s %d points" % (names[-1][:60], len(pts) - n0), flush=True) P = np.array(pts, np.float32) m = (P[:, 0] >= REG[0][0]) & (P[:, 0] <= REG[0][1]) & (P[:, 1] >= REG[1][0]) & (P[:, 1] <= REG[1][1]) & \ (P[:, 2] >= REG[2][0]) & (P[:, 2] <= REG[2][1]) np.savez_compressed(os.path.join(HERE, "obstacles_%s.npz" % tag), points=P[m], ids=np.array(ids, np.int32)[m], names=np.array(names)) print("[ob] written", int(m.sum()), "points of", len(names), "objects", flush=True)