"""Study only (nothing is saved): numbers for a faceted lining of the sub bay. blender -b --factory-startup models/M_Ship_v2_interior_season2.blend --python build/study_bay.py Prints: the carousel's swept radius by height, the lining's and the hull's half-width by height at several stations, what the dock room is made of, and the cross-section of the works hall's wall pieces. """ import json import math import os import sys import bpy import numpy as np sys.dont_write_bytecode = True HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) import shipfit # noqa: E402 out = {} fit = shipfit.Fit(["M_SHIP_Hull", "Moonpool | hull lining", "M_SHIP_Dorsal_Roof", "M_SHIP_Canopy"]) LINING = ("Moonpool | hull lining",) HULL = ("M_SHIP_Hull",) def world_verts(ob): me = ob.data co = np.empty(len(me.vertices) * 3, dtype=np.float64) me.vertices.foreach_get("co", co) m = np.array(ob.matrix_world) return co.reshape(-1, 3) @ m[:3, :3].T + m[:3, 3] # ---- 1. swept radius by height ------------------------------------------------------------------------------------ top = bpy.data.collections["10 INTERIOR | Season 2 layout"] arm_root = next(o for o in bpy.data.objects if o.name.startswith("DockArm_Root")) hub = np.array(arm_root.matrix_world.translation) tend = sorted([o for o in bpy.data.objects if o.name.startswith("_Tender")], key=lambda o: o.name) kz = tend[0].instance_collection kp = np.concatenate([world_verts(o) for o in kz.objects if o.type == "MESH"]) # stowed Tender, own frame: nose -Y, port +X; on the arm: own X is radial, origin 6.0 out and 1.0 below the hub r_t = np.hypot(6.0 + kp[:, 0], kp[:, 1]) r_t2 = np.hypot(6.0 - kp[:, 0], kp[:, 1]) # (either side out: take the larger) z_t = hub[2] - 1.0 + kp[:, 2] parts = [o for o in top.all_objects if o.type == "MESH" and o.data.name.startswith("SM_DockArm")] ap = np.concatenate([world_verts(o) for o in parts]) # arm 1 is lowered in the file: only the two stowed arms describe the stowed sweep; take all and report both r_a = np.hypot(ap[:, 0] - hub[0], ap[:, 1] - hub[1]) z_a = ap[:, 2] zs = np.arange(-3.4, 7.4, 0.2) sweep = [] for z0 in zs: m_t = (z_t >= z0) & (z_t < z0 + 0.2) m_a = (z_a >= z0) & (z_a < z0 + 0.2) sweep.append([round(float(z0), 2), round(float(max(r_t[m_t].max(), r_t2[m_t].max())), 3) if m_t.any() else None, round(float(r_a[m_a].max()), 3) if m_a.any() else None]) out["sweep_r_by_z (z, tender, arm incl. lowered arm 1)"] = sweep out["hub"] = [round(float(v), 3) for v in hub] out["tender_z_range_stowed"] = [round(float(z_t.min()), 3), round(float(z_t.max()), 3)] out["tender_r_max"] = round(float(max(r_t.max(), r_t2.max())), 3) # ---- 2. half-width by height --------------------------------------------------------------------------------------- sect = {} for x in (-9.5, -6.0, -2.0, 3.05, 8.0, 12.0, 15.0): rows = [] for z in np.arange(-3.3, 7.3, 0.4): row = [round(float(z), 2)] for only in (LINING, HULL): a = fit.side_y(float(x), float(z), -1, only) b = fit.side_y(float(x), float(z), 1, only) row += [None if a is None else round(a, 3), None if b is None else round(b, 3)] rows.append(row) sect[str(x)] = rows out["section (z, lining stbd, lining port, hull stbd, hull port)"] = sect roof = {} for x in (-9.0, -4.0, 3.05, 10.0, 15.0): roof[str(x)] = [[y, None if fit.ceil_z(x, y, 0.0, LINING) is None else round(fit.ceil_z(x, y, 0.0, LINING), 3), None if fit.ceil_z(x, y, 0.0, HULL) is None else round(fit.ceil_z(x, y, 0.0, HULL), 3)] for y in (0.0, 2.0, 4.0, 6.0, 7.0, 8.0, 8.5)] out["roof (y, lining z, hull z) at x"] = roof # ---- 3. what the dock room is made of ------------------------------------------------------------------------------ room = [] for col in bpy.data.collections: if col.name.startswith(("03", "02")): for ob in col.all_objects: if ob.type != "MESH": continue v = world_verts(ob) if not len(v): continue room.append({"collection": col.name, "object": ob.name, "verts": len(v), "faces": len(ob.data.polygons), "min": [round(float(c), 2) for c in v.min(0)], "max": [round(float(c), 2) for c in v.max(0)], "materials": [s.material.name if s.material else None for s in ob.material_slots], "modifiers": [m.name for m in ob.modifiers], "hidden": ob.hide_viewport or ob.hide_get()}) out["dock room objects"] = room # ---- 4. the works hall's wall pieces in section ----------------------------------------------------------------------- hall = bpy.data.collections.get("10.2 | Works hall") rows = {} if hall: for ob in hall.objects: if ob.type != "MESH" or not ob.get("ue_mesh"): continue v = world_verts(ob) if abs(v[:, 1]).max() < 6.0: continue key = ob.data.name r = rows.setdefault(key, {"n": 0, "y": [9, -9], "z": [99, -99], "x": [99, -99], "xlen": 0.0}) r["n"] += 1 r["y"] = [round(min(r["y"][0], float(abs(v[:, 1]).min())), 2), round(max(r["y"][1], float(abs(v[:, 1]).max())), 2)] r["z"] = [round(min(r["z"][0], float(v[:, 2].min()) + 3.36), 2), round(max(r["z"][1], float(v[:, 2].max()) + 3.36), 2)] r["xlen"] = round(max(r["xlen"], float(v[:, 0].max() - v[:, 0].min())), 2) out["works hall wall pieces (|y| range, height over the deck, length)"] = rows allv = np.concatenate([world_verts(o) for o in hall.objects if o.type == "MESH" and o.get("ue_mesh")]) if hall else None if allv is not None: out["works hall bounds"] = [[round(float(c), 2) for c in allv.min(0)], [round(float(c), 2) for c in allv.max(0)]] with open(os.path.join(HERE, "study_bay.json"), "w", encoding="utf-8") as f: json.dump(out, f, indent=1) print("STUDY written")