"""Grouper: the cabin inside the skin (read-only; the Tiger's check): crossings between the cabin and the hull or the glass, cabin vertices within 5 mm of them, the least gap, and cabin vertices that can be seen from outside past the hull and the glass along +x, -x or +z (a free line of sight). The lining is part of the cabin mesh and lies 113 mm inside the skin. blender.exe -b Tender_Grouper.blend --python build/gr_cabin_check.py -> build/checks/cabin.json""" import bpy, os, json import numpy as np from mathutils import Vector from mathutils.bvhtree import BVHTree HERE = os.path.dirname(os.path.abspath(__file__)) os.makedirs(os.path.join(HERE, "checks"), exist_ok=True) D = bpy.data dg = bpy.context.evaluated_depsgraph_get() def geo(o): e = o.evaluated_get(dg) me = e.to_mesh() me.calc_loop_triangles() Mx = e.matrix_world vs = [Mx @ v.co for v in me.vertices] ts = [tuple(t.vertices) for t in me.loop_triangles] mi = [t.material_index for t in me.loop_triangles] mats = [m.name if m else None for m in me.materials] e.to_mesh_clear() return vs, ts, mi, mats cv, ct, cmi, cm = geo(D.objects["SM_Grouper_Cabin"]) C = BVHTree.FromPolygons(cv, ct) crossings, near, where = {}, {}, {} trees = [] for n in ("SM_Grouper_Hull", "SM_Grouper_Glass", "SM_Grouper_Door_L", "SM_Grouper_Door_R", "SM_Grouper_Hatch"): hv, ht, hmi, hm = geo(D.objects[n]) H = BVHTree.FromPolygons(hv, ht) trees.append(H) for i, j in C.overlap(H): k = "%s x %s (%s)" % (cm[cmi[i]], hm[hmi[j]], n) crossings[k] = crossings.get(k, 0) + 1 c = (cv[ct[i][0]] + cv[ct[i][1]] + cv[ct[i][2]]) / 3 where.setdefault(k, []).append([round(c.x, 3), round(c.y, 3), round(c.z, 3)]) for q in cv: r = H.find_nearest(q) if r[0] is not None and r[3] < 0.005: near[n] = near.get(n, 0) + 1 least = min(min(t.find_nearest(q)[3] for q in cv) for t in trees) seen = 0 seen_where = [] for q in cv: for dvec in (Vector((1, 0, 0)), Vector((-1, 0, 0)), Vector((0, 0, 1))): if all(t.ray_cast(q + dvec * 0.002, dvec)[0] is None for t in trees): seen += 1 seen_where.append([round(q.x, 3), round(q.y, 3), round(q.z, 3), list(dvec)]) break box = {k: [np.round(np.min(v, 0), 3).tolist(), np.round(np.max(v, 0), 3).tolist()] for k, v in where.items()} out = {"cabin_vertices": len(cv), "crossing_pairs": crossings, "crossing_boxes": box, "vertices_within_5mm": near, "least_gap_m": round(least, 4), "vertices_seen_from_outside": seen, "seen_where": seen_where[:20]} json.dump(out, open(os.path.join(HERE, "checks", "cabin.json"), "w"), indent=1) print("CABIN", json.dumps(out)[:1500])