"""Grouper: the model's own points that the concept render shows plainly, for fitting the concept's camera (gr_concept_fit.py). Read-only: blender -b Tender_Grouper.blend --python build/gr_landmarks.py -> build/checks/landmarks.json""" import bpy, bmesh, os, json from mathutils import Vector HERE = os.path.dirname(os.path.abspath(__file__)) D = bpy.data for ob in D.objects: if ob.animation_data: for fc in ob.animation_data.drivers: fc.driver.expression = fc.driver.expression fc.driver.is_valid = True ob.update_tag() bpy.context.view_layer.update() def islands(o, pred): me = o.data bm = bmesh.new(); bm.from_mesh(me); bm.faces.ensure_lookup_table() todo = {f.index for f in bm.faces if pred(f)} out = [] while todo: s = todo.pop(); isl = [s]; st = [s] while st: f = bm.faces[st.pop()] for e in f.edges: for g in e.link_faces: if g.index in todo: todo.discard(g.index); isl.append(g.index); st.append(g.index) M = o.matrix_world A = sum(bm.faces[i].calc_area() for i in isl) c = sum(((M @ bm.faces[i].calc_center_median()) * bm.faces[i].calc_area() for i in isl), Vector()) / A out.append((A, c)) bm.free() return out P = {} for n in ("SOCKET_Lamp_L", "SOCKET_Lamp_R", "SOCKET_PodLamp_L", "SOCKET_PodLamp_R", "SOCKET_Scanner"): P[n] = list(D.objects[n].matrix_world.translation) gl = sorted(islands(D.objects["SM_Grouper_Glass"], lambda f: True), key=lambda t: -t[0]) for A, c in gl[:6]: print("GLASS island", round(A, 3), [round(v, 3) for v in c]) big = [t for t in gl if t[0] > 0.05] front = min(big, key=lambda t: abs(t[1].x) + 0 * t[1].y) P["front_glass"] = list(front[1]) P["side_glass_R"] = list(min(big, key=lambda t: t[1].x)[1]) P["side_glass_L"] = list(max(big, key=lambda t: t[1].x)[1]) dr = D.objects["SM_Grouper_Door_R"] ai = [i for i, m in enumerate(dr.data.materials) if m and m.name == "M_Grouper_Acrylic"][0] po = islands(dr, lambda f: f.material_index == ai) P["porthole_R"] = list(max(po, key=lambda t: t[0])[1]) hv = [D.objects["SM_Grouper_Hatch"].matrix_world @ v.co for v in D.objects["SM_Grouper_Hatch"].data.vertices] P["hatch_top"] = [0.0, -0.09, max(v.z for v in hv)] hull = D.objects["SM_Grouper_Hull"] duct_y = max((hull.matrix_world @ v.co).y for v in hull.data.vertices) P["duct_aft"] = [0.0, duct_y, -0.0759] rep = json.load(open(os.path.join(HERE, "surfaces_report.json"))) for k in ("lettering_R", "seal_R", "insignia_R"): P[k] = rep["decals"][k]["centre"] P = {k: [round(float(c), 4) for c in v] for k, v in P.items()} json.dump(P, open(os.path.join(HERE, "checks", "landmarks.json"), "w"), indent=1) print("LANDMARKS", json.dumps(P))