"""Checks the pressure hatch through its whole travel (the user's rule: moving parts are checked in every position, not only shut and open). For `open` from 0 to 1 in steps: * every moving part against every static mesh and against every other part (triangles that intersect = a clash); nothing in this hatch is meant to touch: the dogs ride 1 mm over their beds, the cam 3 mm over the back plate, the knuckle turns round the pin with 5 mm all round * the smallest gaps (vertex sampling against the other part's surface) check(steps=100) -> dict """ import bpy, math from mathutils.bvhtree import BVHTree STATIC = ["SM_PressureHatch_Frame", "SM_PressureHatch_Bulkhead"] def _world(ob, dg): ev = ob.evaluated_get(dg) me = ev.to_mesh() M = ob.matrix_world vs = [M @ v.co for v in me.vertices] ps = [tuple(p.vertices) for p in me.polygons] ev.to_mesh_clear() return vs, ps def check(steps=100, near_every=10, set_open=None): movers = ["SM_PressureHatch_Leaf", "SM_PressureHatch_Cam"] + sorted(o.name for o in bpy.data.objects if o.name.startswith("SM_PressureHatch_Dog_")) dg = bpy.context.evaluated_depsgraph_get() statics = {} for n in STATIC: vs, ps = _world(bpy.data.objects[n], dg) statics[n] = BVHTree.FromPolygons(vs, ps) clashes, near = {}, {} for i in range(steps + 1): s = i / steps set_open(s) dg = bpy.context.evaluated_depsgraph_get() geo = {m: _world(bpy.data.objects[m], dg) for m in movers} trees = {m: BVHTree.FromPolygons(*geo[m]) for m in movers} others = dict(statics) pairs = [] for a in movers: for n in STATIC: pairs.append((a, n)) for ia, a in enumerate(movers): for b in movers[ia + 1:]: if a.startswith("SM_PressureHatch_Dog_") and b.startswith("SM_PressureHatch_Dog_"): continue pairs.append((a, b)) for a, b in pairs: tb = others.get(b) or trees[b] ov = trees[a].overlap(tb) if ov: c = clashes.setdefault("%s x %s" % (a, b), {"first": s, "last": s, "max_pairs": 0}) c["last"] = s c["max_pairs"] = max(c["max_pairs"], len(ov)) if i % near_every == 0: for a, b in pairs: if b.startswith("SM_PressureHatch_Dog_") or (a.startswith("SM_PressureHatch_Dog_") and a[-2:] not in ("01", "05")): continue tb = others.get(b) or trees[b] best = 9.0 vs = geo[a][0] stride = 3 if a == "SM_PressureHatch_Leaf" else 1 for p in vs[::stride]: r = tb.find_nearest(p, 0.10) if r[0] is not None and r[3] < best: best = r[3] k = "%s x %s" % (a, b) if best < 9.0 and (k not in near or best < near[k][0]): near[k] = (round(best, 4), s) set_open(0.0) return {"clashes": clashes, "nearest_m_at_open": near}