"""Moving parts of the Remora through their travel against everything that does not move (read only, never saved): canopy 0..-75 deg (5 deg steps), hatch lid 0..-110 deg (10), rotors 0..60 deg (one blade pitch, 3 deg), drill 0..180 deg (10).""" import bpy, math, json, os from mathutils import Matrix, Vector from mathutils.bvhtree import BVHTree HERE = os.path.dirname(os.path.abspath(__file__)) O = bpy.data.objects def tree(o): bpy.context.view_layer.update() me = o.data me.calc_loop_triangles() M = o.matrix_world vs = [M @ v.co for v in me.vertices] return BVHTree.FromPolygons(vs, [tuple(t.vertices) for t in me.loop_triangles]), vs static = [O["SM_Remora_Hull"], O["SM_Remora_Cabin"]] ST = [tree(o) for o in static] def least(vs, step=3): best = 9.0 for t, _ in ST: for p in vs[::step]: h = t.find_nearest(p) if h[0] is not None: best = min(best, h[3]) return best def sweep(name, axis, angles): o = O[name] base = o.rotation_euler.copy() rows = [] for a in angles: e = base.copy() e[axis] = base[axis] + math.radians(a) o.rotation_euler = e t, vs = tree(o) hits = sum(len(t.overlap(s)) for s, _ in ST) rows.append({"deg": a, "intersections": hits, "least_gap_m": round(least(vs), 4)}) o.rotation_euler = base bpy.context.view_layer.update() return {"worst_gap_m": min(r["least_gap_m"] for r in rows), "angles_with_intersections": [r["deg"] for r in rows if r["intersections"]], "rows": rows} out = { "canopy": sweep("SM_Remora_Canopy", 0, list(range(0, -76, -5))), "hatch_lid": sweep("SM_Remora_HatchLid", 0, list(range(0, -111, -10))), "rotor_port": sweep("SM_Remora_Rotor.Port", 1, list(range(0, 61, 3))), "rotor_stbd": sweep("SM_Remora_Rotor.Stbd", 1, list(range(0, 61, 3))), "drill": sweep("SM_Remora_Drill", 1, list(range(0, 181, 10))), } # moving parts against each other at rest (canopy, lid, rotors, drill are far apart; this proves it) movers = ["SM_Remora_Canopy", "SM_Remora_HatchLid", "SM_Remora_Rotor.Port", "SM_Remora_Rotor.Stbd", "SM_Remora_Drill"] mt = {n: tree(O[n])[0] for n in movers} out["movers_touching_each_other"] = [(a, b) for i, a in enumerate(movers) for b in movers[i + 1:] if mt[a].overlap(mt[b])] json.dump(out, open(os.path.join(HERE, "checks", "motion.json"), "w"), indent=1) print("MOTION", json.dumps({k: ({kk: vv for kk, vv in v.items() if kk != "rows"} if isinstance(v, dict) else v) for k, v in out.items()}))