"""Check the six hydraulic cylinders through their whole travel (memory: moving-parts-checked-through-their-travel). The rod's own bar runs inside the barrel by design; everything else on the rod (eye, collars, clevis) must not touch the barrel anywhere from fully out (0) to fully in (stroke). Run inside the built file: blender.exe -b HardSurfaceKit.blend --python check_kit.py""" import json import os import bpy from mathutils import Vector from mathutils.bvhtree import BVHTree def world_tree(ob, keep=None): dg = bpy.context.evaluated_depsgraph_get() me = ob.evaluated_get(dg).data M = ob.matrix_world verts = [M @ v.co for v in me.vertices] polys = [list(p.vertices) for p in me.polygons if keep is None or keep(p, me)] return BVHTree.FromPolygons(verts, polys), len(polys) def run(): out = [] for rod in [o for o in bpy.data.objects if o.name.endswith("_Rod")]: barrel = rod.parent stroke = rod["stroke_mm"] / 1000.0 axis = Vector(rod["slide_axis"]) me = rod.data # the bar's radius: the widest point of what sits inside the barrel (above the gland, local y > 0) bar_r = max(Vector((v.co.x, v.co.z)).length for v in me.vertices if v.co.y > 0.005) def outside(p, m): return any(Vector((m.vertices[i].co.x, m.vertices[i].co.z)).length > bar_r + 0.0005 for i in p.vertices) bt, _ = world_tree(barrel) home = rod.location.copy() worst = None hits = [] steps = 24 for s in range(steps + 1): d = stroke * s / steps rod.location = home + axis * d bpy.context.view_layer.update() rt, n = world_tree(rod, outside) if bt.overlap(rt): hits.append(round(d * 1000, 1)) rod.location = home bpy.context.view_layer.update() out.append(dict(rod=rod.name, stroke_mm=rod["stroke_mm"], bar_radius_mm=round(bar_r * 1000, 1), clashes_at_mm=hits, ok=not hits)) return out res = run() path = os.path.join(os.path.dirname(bpy.data.filepath), "check_report.json") with open(path, "w") as f: json.dump(res, f, indent=1) print("CHECK", json.dumps(res))