"""Critique 2: has the ship changed since the context copy (13:32)? Read-only: both files are opened, never saved. For each of the copied context objects: world matrix, vertex count, world bounds, and the doors' keyframes; compared between models/M_Ship_v2_interior_season2.blend and source/ship_context_2026-10-16.blend. Also lists ship objects near the pockets' upper rails (other mechanisms that ride there). blender -b --factory-startup --python previews/critique2/crit2_ship_vs_context.py -- """ import json import os import sys import bpy from mathutils import Vector OUT = sys.argv[sys.argv.index("--") + 1] HERE = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.dirname(os.path.dirname(HERE)) SHIP = os.path.normpath(os.path.join(ROOT, "..", "M_Ship_v2_interior_season2.blend")) CTX = os.path.join(ROOT, "source", "ship_context_2026-10-16.blend") NAMES = ["Moonpool_Door_Aft", "Moonpool_Door_Fore", "Pocket AFT | upper rail", "Pocket FORE | upper rail", "Pocket AFT | conformal closed end", "Pocket FORE | conformal closed end", "Moonpool | deck around open well", "Moonpool | hull lining", "Moonpool | curved dry basin channel", "Moonpool | titanium aperture rim", "Moonpool | docking lamp", "Moonpool | docking lamp.001", "Moonpool | recessed docking lamp housing", "Moonpool | recessed docking lamp housing.001"] def keys(o): ad = o.animation_data if not ad or not ad.action: return None try: from bpy_extras.anim_utils import action_get_channelbag_for_slot cb = action_get_channelbag_for_slot(ad.action, ad.action_slot) fcs = cb.fcurves if cb else [] except Exception: fcs = getattr(ad.action, "fcurves", []) out = {} for fc in fcs: out["%s[%d]" % (fc.data_path, fc.array_index)] = [[round(k.co.x, 2), round(k.co.y, 4)] for k in fc.keyframe_points] return out def gather(path): bpy.ops.wm.open_mainfile(filepath=path, load_ui=False) dg = bpy.context.evaluated_depsgraph_get() res = {} for n in NAMES: o = bpy.data.objects.get(n) if o is None: res[n] = None continue ev = o.evaluated_get(dg) if o.name in bpy.context.scene.objects else o M = ev.matrix_world bb = [M @ Vector(c) for c in o.bound_box] res[n] = {"loc": [round(x, 4) for x in M.translation], "rot": [round(x, 4) for x in M.to_euler()], "scale": [round(x, 4) for x in M.to_scale()], "verts": len(o.data.vertices) if o.type == 'MESH' else None, "bounds": [[round(min(p[i] for p in bb), 4) for i in range(3)], [round(max(p[i] for p in bb), 4) for i in range(3)]], "keys": keys(o), "mods": [m.type for m in o.modifiers], "basis_loc": [round(x, 4) for x in o.location], "parent": o.parent.name if o.parent else None, "in_scene": o.name in bpy.context.scene.objects} near = [] for o in bpy.data.objects: if o.name in NAMES or o.type not in ('MESH', 'EMPTY', 'ARMATURE', 'CURVE'): continue p = (o.evaluated_get(dg) if o.name in bpy.context.scene.objects else o).matrix_world.translation for x0, x1 in ((-9.2, -2.4), (8.5, 15.3)): if x0 <= p.x <= x1 and -7.2 <= p.y <= -4.3 and -5.5 <= p.z <= -3.3: near.append([o.name, [round(c, 2) for c in p], o.hide_viewport, o.hide_render, [c.name for c in o.users_collection][:2]]) res["_objects_near_the_upper_rails"] = sorted(near)[:80] return res ship = gather(SHIP) ctx = gather(CTX) diff = {} for n in NAMES: a, b = ship.get(n), ctx.get(n) if a != b: diff[n] = {"ship": a, "context": b} R = {"ship_file": SHIP, "differences": diff, "ship_objects_near_rails": ship["_objects_near_the_upper_rails"], "context_objects_near_rails": ctx["_objects_near_the_upper_rails"]} with open(OUT, "w") as fh: json.dump(R, fh, indent=1) print("[crit2] ship vs context written; differing objects:", list(diff), flush=True)