# Mako: the docking interface where the user's red line puts it (over the canopy's rear and the collar), and the hatch. # Exec'd by build_mako.py after mk_fittings.py. # # v03, the user: "holder clips smaller thinner". The docking arm (models/docking-arm) closes four claws round the clamp axis # (0, -0.09) at 45/135/225/315 deg, wrist 1.94 m over the origin. Probed (build/probe_head.py): the claws never come below # z 1.213 (shut: r 0.558-0.693, about 10 deg either side of each diagonal; open: r 0.61-0.79), the landing feet stay at # z 1.405 (r 0.41-0.53). So a clip only needs the Kazah collar's outer profile where a claw closes: a wall at r 0.535 from # z 1.19 to 1.335 and the hook lip (r 0.552, top z 1.375) the claw catches when the arm lifts. Each clip is a curved plate # 3 cm thick and 22 deg long. # v04 (the Critique): no posts on rails over the glass. Each clip stands on a short tapered orange fairing grown out of the # structure under it: the fore pair out of the hood (which the sheet draws over the glass's top rear), the aft pair out of # the bezel's top; only the steel plates read as hardware. The hatch is a flush orange plate behind the dorsal spine. clip = MB("_clamp", [STEEL, GRAPH, YELLOW, PAINT]) CL_C = Vector((0.0, Y_CL, 0.0)) CLIP_AZ = (45.0, 135.0, 225.0, 315.0) CLIP_RI, CLIP_RO, CLIP_LIP = 0.506, 0.535, 0.552 CLIP_Z0, CLIP_ZL, CLIP_ZT = 1.188, 1.335, 1.375 def sweep_arc(mb, prof_at, a0, a1, segs, mat): """an 8-point profile that may change along the arc: prof_at(u) -> [(r, z)], u from 0 to 1; ends closed by three quads""" bm2 = bmesh.new() cols = [] for j in range(segs + 1): u = j / segs ang = a0 + (a1 - a0) * u cols.append([bm2.verts.new((CL_C.x + r * math.cos(ang), CL_C.y + r * math.sin(ang), z)) for (r, z) in prof_at(u)]) n = len(cols[0]) for j in range(segs): for q in range(n): bm2.faces.new((cols[j][q], cols[j + 1][q], cols[j + 1][(q + 1) % n], cols[j][(q + 1) % n])) for c in (cols[0], cols[-1]): bm2.faces.new((c[0], c[1], c[2], c[7])); bm2.faces.new((c[7], c[2], c[3], c[6])); bm2.faces.new((c[6], c[3], c[4], c[5])) bm2.normal_update() bmesh.ops.recalc_face_normals(bm2, faces=bm2.faces[:]) mb.merge(bm2, mat) def post_loft(mb, secs, mat): """a post through chamfered-rectangle sections [(centre, chord dir, half chord, half thickness, chamfer)], stacked upward; three quads on each end""" bm2 = bmesh.new() rings = [] for (c, cd, hc, ht, ch) in secs: td = Vector((0, 0, 1)).cross(cd).normalized() pts = [(-hc + ch, -ht), (hc - ch, -ht), (hc, -ht + ch), (hc, ht - ch), (hc - ch, ht), (-hc + ch, ht), (-hc, ht - ch), (-hc, -ht + ch)] rings.append([bm2.verts.new(c + cd * u + td * v) for u, v in pts]) ring_rows(bm2, rings) for r in (rings[0], rings[-1]): bm2.faces.new((r[7], r[0], r[1], r[2])); bm2.faces.new((r[7], r[2], r[3], r[6])); bm2.faces.new((r[6], r[3], r[4], r[5])) bm2.normal_update() bmesh.ops.recalc_face_normals(bm2, faces=bm2.faces[:]) mb.merge(bm2, mat) CLIP_HALF = math.radians(11.0) # the claw's pad spans about 10 deg either side of its diagonal (probe_head.py) CHAMFER = 0.18 # the head's top corners fall away over the last 18 % of the arc at each end CLIP_R = 0.5205 # the plate's middle radius CLIP_INFO = {} for az in CLIP_AZ: a = math.radians(az) fore = math.sin(a) < 0 cx, cy = CL_C.x + CLIP_R * math.cos(a), CL_C.y + CLIP_R * math.sin(a) tang = Vector((-math.sin(a), math.cos(a), 0)) # the fairing: from 4 cm inside the hood (fore) or the bezel (aft) up under the plate; a broad foot (a fairing, not a # stick) tapering to the plate's 3 cm thickness and its arc's chord at the top # its foot runs along the hull (the brow's or the shell's length), a long crest (round 2: the sheet's hood carries the clip # as one smooth orange shape), turning into the clip's arc toward the top chord = 2 * CLIP_R * math.sin(CLIP_HALF) * 0.5 foot_hc = 0.21 if fore else 0.16 foot_y = (Yi(272.0) if fore else cy + 0.04) zs = min((brow_top(cx + dx, foot_y + dy) if fore else hull_z_at(cx + dx, foot_y + dy)) for dx in (-0.03, 0.03) for dy in (-foot_hc, -0.5 * foot_hc, 0.0, 0.5 * foot_hc, foot_hc)) z_foot, z_top = zs - 0.045, CLIP_Z0 + 0.014 t_dir = tang if tang.y > 0 else -tang secs = [] for k in range(9): t = k / 8 z = z_foot + (z_top - z_foot) * t e = smoothstep(0.0, 1.0, t) cd = Vector((0, 1, 0)).lerp(t_dir, smoothstep(0.55, 1.0, t)).normalized() c = Vector((cx, foot_y + (cy - foot_y) * smoothstep(0.0, 1.0, t), z)) secs.append((c, cd, chord + (foot_hc - chord) * (1 - e) ** 1.2, 0.0145 + 0.042 * (1 - e) ** 1.4, 0.008)) post_loft(clip, secs, PAINT) z0 = CLIP_Z0 def prof_at(u, z0=z0): e = max(0.0, abs(2 * u - 1) - (1 - 2 * CHAMFER)) / (2 * CHAMFER) # 0 in the middle, 1 at the arc's ends dz = 0.030 * e * e return [(CLIP_RI, z0), (CLIP_RO, z0), (CLIP_RO, CLIP_ZL - dz), (0.546, 1.348 - dz), (CLIP_LIP, 1.362 - dz), (0.546, CLIP_ZT - dz), (CLIP_RI, CLIP_ZT - dz), (CLIP_RI, 0.5 * (z0 + CLIP_ZT))] sweep_arc(clip, prof_at, a - CLIP_HALF, a + CLIP_HALF, 8, STEEL) qbox(clip, (0.022, 0.05, 0.004), (CL_C.x + 0.521 * math.cos(a), CL_C.y + 0.521 * math.sin(a), CLIP_ZT + 0.002), YELLOW, rot=(0, 0, a)) CLIP_INFO["%d" % az] = dict(fore=fore, centre=[round(cx, 3), round(cy, 3)], stands_on_z=round(zs, 3), fairing_m=round(CLIP_Z0 - zs, 3), height_m=round(CLIP_ZT - zs, 3), arc_deg=round(2 * math.degrees(CLIP_HALF), 1), plate_m=round(CLIP_RO - CLIP_RI, 3), on=("brow" if fore else "shell behind the collar")) REPORT["clips"] = CLIP_INFO # ------------------------------------------------------------------ the hatch (v04: flush, an orange plate behind the dorsal spine) # The hull's hatch hole (mk_hull.py: whole grid faces, a sleeve down to the lining) is filled by the lid: the hole's own grid at # the skin, drawn in 8 mm all round (the gap), 25 mm thick. It is hinged along its starboard edge on two small knuckles 3 cm # over the skin and lifts its port edge. HC0 = Vector((0.0, Y_HT, hull_z_at(0.0, Y_HT))) HN = normal_at(HC0) HTAN = Vector((0, 1, 0)); HTAN = (HTAN - HTAN.dot(HN) * HN).normalized() HX = Vector((1, 0, 0)) def hl(lx, ly, lz): """a point in the hatch's frame: x across, y along the back (aft), z out of the hull""" return HC0 + HX * lx + HTAN * ly + HN * lz hole = [(i, k) for (i, k), r in ROLE.items() if r == "hatch"] LID_GAP, LID_T = 0.008, 0.025 hatch = MB("SM_Mako_Hatch", [PAINT, GRAPH, STEEL, YELLOW]) bml = bmesh.new() def lid_top(i, k): p = P((i, k % M)) d = p - HC0 lx, ly = d.dot(HX), d.dot(HTAN) r = max(abs(lx), abs(ly), 1e-6) s = max(0.0, (r - LID_GAP)) / r return HC0 + HX * (lx * s) + HTAN * (ly * s) + HN * d.dot(HN) TV, BV = {}, {} def tv(i, k): k %= M if (i, k) not in TV: TV[(i, k)] = bml.verts.new(lid_top(i, k)) BV[(i, k)] = bml.verts.new(lid_top(i, k) - sleeve_dir("hatch", i, k) * LID_T) # parallel to the hole's walls return TV[(i, k)] for (i, k) in hole: q = [(i, k), (i, k + 1), (i + 1, k + 1), (i + 1, k)] bml.faces.new([tv(*a) for a in q]) bml.faces.new([BV[(a[0], a[1] % M)] for a in q[::-1]]) for loop in LOOPS["hatch"]: # the lid's edge n = len(loop) for j in range(n): a, b = loop[j], loop[(j + 1) % n] tv(*a); tv(*b) bml.faces.new((TV[(a[0], a[1] % M)], BV[(a[0], a[1] % M)], BV[(b[0], b[1] % M)], TV[(b[0], b[1] % M)])) bml.normal_update() bmesh.ops.recalc_face_normals(bml, faces=bml.faces[:]) bml.normal_update() pf = max(bml.faces, key=lambda f: (f.calc_center_median() - HC0).dot(HN)) if pf.normal.dot(HN) < 0: bmesh.ops.reverse_faces(bml, faces=bml.faces[:]) for f in bml.faces: f.material_index = 0 hatch.merge(bml, None) # v04: hinged along its starboard edge (a pin along the hull's length, 3 cm over the skin), it lifts its port edge: the lid is a # curved plate on the rounded back, and a hinge across its fore edge swung its low outer corners into the hole's aft edge lat_h = -(HATCH_R + 0.035) ys_h = [Y_HT - 0.20 + 0.40 * t / 8 for t in range(9)] z_h = max(hull_z_at(lat_h, y) for y in ys_h) + 0.030 HATCH_HINGE = Vector((lat_h, Y_HT, z_h)) HATCH_AXIS = "Y" # Blender rotation_euler.y = -deg lifts the port edge hatch_fx = MB("_hatchfix", [GRAPH, PAINT, STEEL, YELLOW]) for sy in (-0.15, 0.15): # the knuckles: a bracket on the hull, one on the lid, a pin yb_ = Y_HT + sy - 0.03 * (1 if sy > 0 else -1) zb = hull_z_at(lat_h, yb_) qbox(hatch_fx, (0.05, 0.035, HATCH_HINGE.z - zb + 0.03), (lat_h - 0.005, yb_, 0.5 * (HATCH_HINGE.z + zb) - 0.005), GRAPH, bevel=0.006) qcyl(hatch_fx, 0.009, 0.10, Vector((lat_h, Y_HT + sy, z_h)), STEEL, rot=(math.pi / 2, 0, 0), segs=8) ky = Y_HT + sy + 0.02 * (1 if sy > 0 else -1) qcyl(hatch, 0.016, 0.035, Vector((lat_h, ky, z_h)), GRAPH, rot=(math.pi / 2, 0, 0), segs=8) p0 = Vector((lat_h, ky, z_h)) # an arm from the knuckle in onto the lid, over the skin p1 = Vector((lat_h + 0.12, ky, hull_z_at(lat_h + 0.12, ky) + 0.009)) ax_ = (p1 - p0).normalized() vd = Vector((0, 1, 0)).cross(ax_).normalized() if vd.z < 0: vd = -vd rect_loft(hatch, [(p0 + ax_ * 0.026, Vector((0, 1, 0)), vd, 0.015, 0.009, 0.003), # from the knuckle (clear of the pin inside it) (p1, Vector((0, 1, 0)), vd, 0.015, 0.006, 0.002)], GRAPH) qbox(hatch, (0.12, 0.03, 0.010), hl(0.0, HATCH_R - 0.08, 0.002), GRAPH, bevel=0.003) # a flush grip recess (a dark plate) HATCH_MAX = 100.0 REPORT["stage"] = "clamp+hatch"