"""Door robot arm: lower arm (J2..J3), the J3 cylinder and its piston, the upper arm (J3..J4). Coordinates: each builder works in its own link frame (A axes, origin on the link's joint) and sets L.base to that origin in the arm frame A. Lower-arm frame: origin J2; x forward, z up; the lower arm's outline is drawn in x-z. Changes to the design, made here (reasons in STATUS.md): - drum diameter 0.49 (design 0.594): a 0.594 drum turning on J2 cuts 22 mm into the turret's front housing; the white ring round the drum in ref1 is a fixed collar on the turret (dra_parts_base); - the lower arm is built in layers: a narrow beam (|y| <= 0.175: two inner shells round a dark core with the cylinder's channel) from the drum to the crotch, and wide outer shells (|y| 0.17..0.451) only beyond 0.40 m from J2, so the arm can lean 58 degrees forward past the front housing and the motor pods (the door needs 53); - the trunnion pins are stubs on the cylinder's collar turning in bosses on the channel walls (no pin through the barrel); the knob pin is 0.046 (the design's 0.11 does not fit the rod eye's 0.05 bore); - the channel floor drops from 0.095 to 0.150 below the rod axis beyond the knee: the cylinder turns up to 5.2 degrees toward the core and the gland would touch a flat floor. """ import math import bmesh from mathutils import Vector, Matrix from dra_lib import (Link, lathe, slab, rbox, tube, knurl, star_slab, merge, xform, frame, basis, mirror_y, holed_surface, W, G, S, R, O, GL, LA, TAU, K, circle2, decal_strip, decal_poly, decal_dot, fillet3d, stack, sd_circle, sd_hull, sd_union, sd_inter, sd_minus, sd_half, contour, inset_poly, split_poly, ccw, face, bridge, fillet) from dra_shapes import (rrect, stadium, xz_slab, yz_slab, xy_slab, screw, dome, hexbolt, ring, cyl, grooved_cyl, holed_plate, notched_disc, half_discs, quarter_discs, u_handle, ribbed_boot, thread_rod, M_XZ) import dra_rigdata as rd V2 = lambda x, y: Vector((x, y)) # ---------------------------------------------------------------- lower-arm frame numbers J2A = rd.J2 T2 = V2(-0.5236, 0.330) # trunnion / knee J3L = V2(-0.869, 1.309) # J3 in the lower-arm frame U2 = V2(-0.7997, 0.6003) # cylinder axis (toward the knob) NB = V2(-0.6003, -0.7997) # from the cylinder axis toward the channel opening (the back) KD = (J3L - T2).normalized() # knee -> J3 KP = V2(KD.y, -KD.x) # across the band, toward the front C0 = J3L - KD * 0.571 # the yoke's crotch DRUM_R = 0.247 # barrel radius at its middle (it bulges; 0.236 at the ends) DRUM_R_END = 0.236 DRUM_HW = 0.200 # half length (ref7: about 0.42 m long with the hubs; CRITIQUE_1 item 3) STRAP_Y = (0.045, 0.130) # the root straps over the barrel (ref7), |y| STRAP_R = (0.251, 0.305) # their radii round J2 (ref1: the white ring round the drum, r 0.26..0.31) STRAP_ARC = (95.0, 228.0) # degrees round J2 (from +x toward +z), rest pose SKIN_Y = (0.345, 0.430) # outer shells: skin PANEL_TOP = 0.446 # raised panels 16 mm proud of the skin (CRITIQUE_1 item 5: real steps) RIM_TOP = 0.458 # the raised rim round the shell's edges, 12 mm over the panels # the dark core (stage 3 shape pass, ref5 / ref1 / ref2): one dark mass in front of the white shells from the knee to # the crotch, and one raised face on it (how far in front of the shells' front edge, half width, edge round) CORE_MASS = (0.072, 0.285, 0.016) CORE_FACE = (0.094, 0.190, 0.006) ROOT_CLEAR = 0.315 # the white inner shells start this far from J2: the drum shows (ref7) def regions(): """signed distance functions (x, z) of the lower arm's layers""" Fc, Bc = C0 + KP * 0.19, C0 - KP * 0.19 beam = sd_union(sd_hull([(V2(0, 0), 0.20), (T2, 0.25)]), sd_hull([(T2, 0.25)], [Fc, Bc])) below = sd_half(C0, KD) # inside where (p - C0).KD < 0 lower_band = sd_hull([(T2, 0.275)], [C0 + KD * 0.05 + KP * 0.20, C0 + KD * 0.05 - KP * 0.20]) cheek_band = sd_hull([(J3L, 0.17)], [C0 - KD * 0.05 + KP * 0.20, C0 - KD * 0.05 - KP * 0.20]) beyond = lambda p: 0.40 - p.length # outside 0.40 m from J2 # CRITIQUE_1 item 5: an S-curved front edge (convex round the knee, a shallow hollow, convex at the crotch, ref1) fa, fb = T2 + KP * 0.275, C0 + KD * 0.05 + KP * 0.20 mid = (fa + fb) / 2 dn = fb - fa nrm = V2(dn.y, -dn.x).normalized() if nrm.dot(KP) < 0: nrm = -nrm carve = sd_circle(mid + nrm * (0.55 - 0.045), 0.55) band = sd_minus(sd_union(lower_band, cheek_band), carve) outer = sd_inter(band, beyond) outer_low = sd_inter(outer, below) outer_cheek = sd_inter(outer, lambda p: -((p - C0).dot(KD) + 0.03)) inner = sd_inter(beam, below) def floor_off(s): if s <= 0.25: return -0.095 if s >= 0.60: return -0.150 return -0.095 - 0.055 * (s - 0.25) / 0.35 core = sd_inter(lambda p: inner(p) + 0.040, lambda p: (p - T2).dot(NB) - floor_off((p - T2).dot(U2))) def front_core(off): """the dark core's stacked plates standing 'off' in front of the shells' front edge, from just above the knee boss to the crotch (ref1 x_img 1270-1450, y_img 770-1100; ref2)""" return sd_inter(lambda p: band(p) - off, lambda p: 0.06 - (p - T2).dot(KP), # the front part only lambda p: (T2 - p).dot(KD) + 0.05, # above the knee's middle lambda p: (p - C0).dot(KD) - 0.02, # below the crotch lambda p: 0.58 - p.length) # from the knee up (clear of the turret) inner_shell = sd_inter(inner, lambda p: ROOT_CLEAR - p.length) return dict(inner=inner, inner_shell=inner_shell, outer=outer, outer_low=outer_low, outer_cheek=outer_cheek, core=core, floor_off=floor_off, front_core=front_core) BBOX = (-1.15, -0.30, 0.30, 1.52) def _outline(f, cell=0.004, tol=0.0014): return [(p.x, p.y, 0.0) for p in contour(f, BBOX, cell, tol)] def _panels(L, outline, y_face, depth, seams, pid, mat=W, side=(1, -1), inset=0.006, gap=0.004, bev=0.0022, seats=(), mats=None): """raised panels on one face of an x-z shell (y_face > 0 for the +y face; mirrored for side -1)""" from dra_lib import fillet as _f P = ccw([Vector((p[0], p[1])) for p in outline]) P = inset_poly(P, inset) pieces = [P] for (a, b) in seams: nxt = [] for pc in pieces: l, r = split_poly(pc, a, b, gap) for q in (l, r): if len(q) >= 3 and abs(sum(q[i - 1].x * q[i].y - q[i].x * q[i - 1].y for i in range(len(q)))) > 2e-4: nxt.append(q) pieces = nxt for s in side: for k, pc in enumerate(pieces): o = [(p.x, p.y, 0.0) for p in pc] m = mat if mats is None else mats(pc) bm = xz_slab(o, y_face - depth - 0.002, y_face, 0.0, bev, n=1) if s < 0: mirror_y(bm) L.put(bm, mat=m, pid=pid + ('.L' if s > 0 else '.R'), seats=seats) return pieces def drum_r(y): """barrel radius at y: a slight bulge (0.247 in the middle, 0.236 at the ends)""" t = min(1.0, abs(y) / DRUM_HW) return DRUM_R_END + (DRUM_R - DRUM_R_END) * (1.0 - t * t) def _drum(L): """L01: the perforated J2 drum (axis y), CRITIQUE_1 item 3: a bulging barrel 0.40 m long with a ring of 32 blind holes near each end (ref7: one row each side of the root straps), side faces with a ring of 32 holes (ref3) and a low stepped hub; one closed shell""" bm = bmesh.new() n = 32 us = [TAU * i / n for i in range(n + 1)] ys = [-0.196, -0.180, -0.146, -0.073, 0.0, 0.073, 0.146, 0.180, 0.196] # the middle is under the straps holes = {(i, 1) for i in range(n)} | {(i, 6) for i in range(n)} def mp(u, v): r = drum_r(v) return Vector((r * math.cos(u), v, r * math.sin(u))) holed_surface(bm, mp, us, ys, holes, 0.0120, 0.012, lambda u, v: Vector((math.cos(u), 0, math.sin(u))), periodic_u=True, mat=G) for s in (1, -1): rs = [0.226, 0.214, 0.186, 0.150] if s > 0 else [0.150, 0.186, 0.214, 0.226] holed_surface(bm, lambda u, v, s=s: Vector((v * math.cos(u), s * DRUM_HW, v * math.sin(u))), us, rs, {(i, 1) for i in range(n)}, 0.0095, 0.010, lambda u, v, s=s: Vector((0, s, 0)), periodic_u=True, mat=G) piece = bmesh.new() merge(piece, bm) bm.free() re = drum_r(0.196) for s in (1, -1): fil = lathe([(re, 0.196, 0, G), (0.2340, 0.1984, 0, G), (0.2305, 0.1996, 0, G), (0.226, DRUM_HW, 0, G)], 2 * n, n=1) hub = lathe([(0.150, DRUM_HW, 0, G), (0.138, DRUM_HW, 0, G), (0.136, DRUM_HW + 0.004, 0, G), (0.100, DRUM_HW + 0.004, 0, G), (0.096, DRUM_HW + 0.002, 0, G), (0.072, DRUM_HW + 0.002, 0, G), (0.070, DRUM_HW + 0.007, 0.001, S), (0.030, DRUM_HW + 0.007, 0, S), (0.030, DRUM_HW + 0.005, 0, G), (0.0, DRUM_HW + 0.005, 0, G)], 2 * n, n=1) for p in (fil, hub): M = frame((0, 0, 0), 'Y') if s > 0 else frame((0, 0, 0), '-Y') xform(p, M) merge(piece, p) p.free() bmesh.ops.remove_doubles(piece, verts=piece.verts[:], dist=2e-5) bmesh.ops.recalc_face_normals(piece, faces=piece.faces[:]) L.put(piece, None, None, 'L01') def arc_sweep(prof, a0, a1, n, mat=W): """a closed 2D profile (r, y) swept round the y axis from angle a0 to a1 (radians, from +x toward +z), with flat end caps; returns a bmesh""" from dra_lib import fill_cap P = ccw([Vector(p) for p in prof]) bm = bmesh.new() rings = [] for k in range(n + 1): a = a0 + (a1 - a0) * k / n ca, sa = math.cos(a), math.sin(a) rings.append([bm.verts.new((p.x * ca, p.y, p.x * sa)) for p in P]) m = len(P) for k in range(n): A, B = rings[k], rings[k + 1] am = a0 + (a1 - a0) * (k + 0.5) / n for j in range(m): j2 = (j + 1) % m e = P[j2] - P[j] o2 = Vector((e.y, -e.x)) # outward in (r, y) for a CCW profile o = Vector((o2.x * math.cos(am), o2.y, o2.x * math.sin(am))) face(bm, [A[j], A[j2], B[j2], B[j]], mat, out=o) t0 = Vector((-math.sin(a0), 0, math.cos(a0))) fill_cap(bm, rings[0], -t0, mat) t1 = Vector((-math.sin(a1), 0, math.cos(a1))) fill_cap(bm, rings[-1], t1, mat) bmesh.ops.recalc_face_normals(bm, faces=bm.faces[:]) return bm def _root_band(L): """CRITIQUE_1 item 3: the lower arm's root over the drum's back (ref7) = two white straps either side of a ribbed centre strip, wrapping the barrel round J2 from the top to the bottom-back (in a side view the white ring round the drum, ref1 and ref3). Centred on J2, so it sweeps no new space when the arm turns.""" r0, r1 = STRAP_R y0, y1 = STRAP_Y a0, a1 = math.radians(STRAP_ARC[0]), math.radians(STRAP_ARC[1]) prof = [(r0, y0), (r1 - 0.012, y0), (r1, y0 + 0.012), (r1, y1 - 0.012), (r1 - 0.012, y1), (r0, y1)] rounded = [p for p, _ in fillet([(x, y, 0.004 if i in (1, 2, 3, 4) else 0.0) for i, (x, y) in enumerate(prof)], closed=True, n=2)] for s in (1, -1): pr = [(p.x, s * p.y) for p in rounded] bm = arc_sweep(pr, a0, a1, 40, W) L.put(bm, None, W, 'L02' + ('.L' if s > 0 else '.R'), seats=('L01', 'L03.L', 'L03.R', 'L03c')) # a groove line along each strap (ref7): a thin dark inlay gp = [(r1 - 0.0005, s * (y0 + 0.040)), (r1 + 0.0012, s * (y0 + 0.040)), (r1 + 0.0012, s * (y0 + 0.044)), (r1 - 0.0005, s * (y0 + 0.044))] L.put(arc_sweep(gp, a0 + 0.03, a1 - 0.03, 40, G), None, G, 'L02g', seats=('L02.L', 'L02.R', 'L03.L', 'L03.R')) # the ribbed centre strip between the straps: cross rungs standing on the barrel k = 0 a = a0 + math.radians(2.0) while a < a1 - math.radians(1.5): ca, sa = math.cos(a), math.sin(a) M = Matrix(((-sa, 0, ca), (0, 1, 0), (ca, 0, sa))).to_4x4() # local x along the arc, z outward M.translation = Vector(((DRUM_R + 0.0085) * ca, 0, (DRUM_R + 0.0085) * sa)) L.put(rbox((0.012, 2 * y0 + 0.004, 0.019), 0.002, segs=1), M, G, 'L05d', seats=('L01', 'L02.L', 'L02.R')) a += math.radians(4.4) k += 1 def build_lower_arm(): L = Link('lower arm') L.base = J2A.copy() rg = regions() _drum(L) _root_band(L) # inner shells (two side walls of the beam, |y| 0.075..0.175) and the dark core between them o_inner = _outline(rg['inner_shell']) # stage 3: not round the drum (the root straps and the core carry it) for s in (1, -1): bm = xz_slab(o_inner, 0.075, 0.175, 0.003, 0.010) if s < 0: mirror_y(bm) L.put(bm, mat=W, pid='L03' + ('.L' if s > 0 else '.R'), seats=('L01', 'L03c', 'L02')) o_core = _outline(rg['core']) L.put(xz_slab(o_core, -0.0765, 0.0765, 0.004, 0.004), mat=G, pid='L03c', seats=('L01', 'L03', 'L02', 'L05d')) # outer shells beyond 0.40 m from J2: skin |y| 0.345..0.430, fills behind it; the cheeks' inner faces at 0.275 o_outer = _outline(rg['outer']) o_low = _outline(rg['outer_low']) o_cheek = _outline(rg['outer_cheek']) for s in (1, -1): sd = '.L' if s > 0 else '.R' for o, y0, y1, b0, b1, pid in ((o_outer, SKIN_Y[0], SKIN_Y[1], 0.004, 0.006, 'L04s'), (o_low, 0.170, 0.350, 0.006, 0.004, 'L04f'), (o_cheek, 0.275, 0.350, 0.008, 0.004, 'L14')): bm = xz_slab(o, y0, y1, b0, b1) if s < 0: mirror_y(bm) L.put(bm, mat=W, pid=pid + sd, seats=('L03' + sd, 'L04s' + sd, 'L04f' + sd, 'L14' + sd, 'L04r' + sd)) # CRITIQUE_1 item 5: a raised rim round the shell's edges (ref1, ref5) and recessed faces inside it Po = ccw([Vector((p[0], p[1])) for p in o_outer]) Pin = inset_poly(Po, 0.026) for s in (1, -1): sd = '.L' if s > 0 else '.R' bm = xz_slab([(p.x, p.y, 0.0) for p in Po], SKIN_Y[1] - 0.004, RIM_TOP, 0.0, 0.0045, n=1, holes=[[(p.x, p.y, 0.0) for p in Pin]]) if s < 0: mirror_y(bm) L.put(bm, mat=W, pid='L04r' + sd, seats=('L04s' + sd,)) # the dark core: one dark mass between and in front of the white shells (ref5, ref1, ref2), well rounded, and one # raised face on its front that carries the bosses for k, (off, hw, bev) in enumerate((CORE_MASS, CORE_FACE)): oc = _outline(rg['front_core'](off)) if len(oc) < 3: continue L.put(xz_slab(oc, -hw, hw, bev, bev, steps=3), mat=G, pid='L03f%d' % k, seats=('L03', 'L03c', 'L04f', 'L04s', 'L03f0', 'L03f1', 'L04r', 'L14')) # recessed faces: panels inside the rim, cut by seams (across above the knee, across above the crotch, along the # back strip, a strip along the front, the diagonal groove up from the knee of ref5); 16 mm proud of the skin a1 = T2 + KD * 0.30 a2 = C0 + KD * 0.10 b0 = C0 - KP * 0.085 f0 = C0 + KP * 0.115 # a strip along the front edge d45 = (KD + KP).normalized() # the diagonal groove up from the knee (ref5) q45 = T2 + KD * 0.40 seams = [(a1 - KP, a1 + KP), (a2 - KP, a2 + KP), (b0 - KD * 2, b0 + KD * 2), (f0 - KD * 2, f0 + KD * 2), (q45 - d45 * 0.6, q45 + d45 * 0.6)] _panels(L, [(p.x, p.y, 0.0) for p in Pin], PANEL_TOP, PANEL_TOP - SKIN_Y[1], seams, 'L12', inset=0.004, gap=0.005, bev=0.003, seats=('L04s', 'L04r')) # stepped notch block at the top of the knee (ref5): a small raised block with 45-degree ends nb = T2 + KD * 0.27 + KP * 0.10 blk = [(-0.045, -0.025, 0.0), (0.045, -0.025, 0.0), (0.065, 0.0, 0.0), (0.045, 0.025, 0.0), (-0.045, 0.025, 0.0)] ang = math.atan2(KP.y, KP.x) ca, sa = math.cos(ang), math.sin(ang) blk = [(nb.x + x * ca - y * sa, nb.y + x * sa + y * ca, r) for x, y, r in blk] for s in (1, -1): bm = xz_slab(blk, PANEL_TOP - 0.002, PANEL_TOP + 0.014, 0.0, 0.002, n=1) if s < 0: mirror_y(bm) sd_ = '.L' if s > 0 else '.R' L.put(bm, mat=W, pid='L12b' + sd_, seats=('L12' + sd_, 'L06' + sd_, 'L04r' + sd_)) _knee(L) _elbow(L) _channel(L, rg) _front_ribs(L, rg) _handles(L) _left_side_details(L) _front_core_details(L) return L def _front_core_details(L): """L18: on the dark core in the front recess near the crotch: two round bosses with bolts and an inner round cap""" kp3 = Vector((KP.x, 0, KP.y)) for (t, y, kind) in ((-0.10, 0.034, 'boss'), (-0.10, -0.034, 'boss'), (-0.22, 0.0, 'cap')): p = C0 + KD * t + KP * 0.6 hit, n = L.snap((p.x, y, p.y), -kp3) if hit is None: continue if kind == 'boss': L.put(stack([(0.044, 0.010, 'gunmetal'), (0.024, 0.006, 'steel')], 20, base=0.002), frame(hit, n), None, 'L18', seats=('L03c', 'L03f0', 'L03f1', 'L03f2')) else: L.put(stack([(0.099, 0.008, 'gunmetal'), (0.080, 0.006, 'gunmetal'), (0.040, 0.008, 'steel')], 28, base=0.002), frame(hit, n), None, 'L18', seats=('L03c', 'L03f0', 'L03f1', 'L03f2')) def slotted_disc(r, n, w, depth, n_arc=4, n_end=4, phase=0.0): """a disc outline (angular order) with n short notches cut in from the rim, each with a round inner end""" pts = [] pa = TAU / n hw = w / 2 half = math.asin(hw / r) rr = math.sqrt(r * r - hw * hw) e = r - depth + hw # centre of the round end for k in range(n): c = phase + k * pa a0, a1 = c + half, c + pa - half for j in range(n_arc + 1): a = a0 + (a1 - a0) * j / n_arc pts.append((r * math.cos(a), r * math.sin(a))) cn = c + pa d = Vector((math.cos(cn), math.sin(cn))) t = Vector((-d.y, d.x)) pts.append(tuple(d * rr - t * hw)) for j in range(n_end + 1): ph = math.pi * j / n_end q = d * e + (-t * math.cos(ph) - d * math.sin(ph)) * hw pts.append((q.x, q.y)) pts.append(tuple(d * rr + t * hw)) out = [] for p in pts: if not out or (Vector(out[-1]) - Vector(p)).length > 1e-7: out.append(p) if (Vector(out[0]) - Vector(out[-1])).length < 1e-7: out.pop() return out def _knee(L): """L06 white boss ring and L07 knee cap on both sides (CRITIQUE_1 item 6, ref1 x 1295-1485, y 1075-1260 and ref5): ring 0.41 m, a dark disc 0.30 m set 8 mm down in it with ten short round-ended notches at its rim, a castellated inner ring 0.165 m round the hub, a thin ORANGE ring and the bolt. They cover the J3 cylinder's trunnion bearing.""" base = PANEL_TOP - 0.003 for s in (1, -1): sd = '.L' if s > 0 else '.R' Mk = frame((T2.x, s * base, T2.y), (0, s, 0)) L.put(ring(0.150, 0.205, 0.026, 56, 0.004, W), Mk, W, 'L06' + sd, seats=('L12' + sd, 'L04s' + sd, 'L04r' + sd)) disc = star_slab(slotted_disc(0.150, 10, 0.024, 0.046, n_arc=4, n_end=4, phase=math.radians(9)), 0.0, 0.018, hub_r=0.090, bev=0.0015, mat=G) L.put(disc, Mk, G, 'L07' + sd, seats=('L12' + sd, 'L04s' + sd, 'L06' + sd)) from dra_shapes import gear_outline cast = star_slab(gear_outline(12, 0.066, 0.0825, 0.30, 0.20), 0.016, 0.032, hub_r=0.045, bev=0.0012, mat=G) L.put(cast, Mk, G, 'L07c' + sd, seats=('L07' + sd,)) st = stack([(0.120, 0.006, 'gunmetal'), (0.098, 0.003, 'orange'), (0.088, 0.010, 'gunmetal'), (0.060, 0.006, 'gunmetal'), (0.040, 0.012, 'steel')], 40, base=0.002) L.put(st, Mk @ Matrix.Translation((0, 0, 0.032)), None, 'L07h' + sd, seats=('L07c' + sd, 'L07' + sd)) for k in range(8): a = TAU * (k + 0.5) / 8 p = Vector((T2.x + 0.178 * math.cos(a), s * (base + 0.026), T2.y + 0.178 * math.sin(a))) L.put(screw(0.0055), frame(p, (0, s, 0)), None, 'L07s' + sd, seats=('L06' + sd,)) def _elbow(L): """L15 elbow caps: white bezel, dark ring, a black cap with a straight slot ('minus' head) on a steel disc, two ticks""" for s in (1, -1): sd = '.L' if s > 0 else '.R' Me = frame((J3L.x, s * (PANEL_TOP - 0.003), J3L.y), (0, s, 0), spin=math.radians(-20) * s) L.put(ring(0.122, 0.154, 0.026, 36, 0.004, W), Me, W, 'L15' + sd, seats=('L12' + sd, 'L04s' + sd, 'L04r' + sd)) L.put(stack([(0.244, 0.012, 'gunmetal'), (0.206, 0.004, 'steel')], 36, base=0.002), Me, None, 'L15' + sd, seats=('L15' + sd, 'L12' + sd, 'L04s' + sd, 'L04r' + sd)) L.put(half_discs(0.103, 0.026, 0.022, G, segs=12, bev=0.0015, z0=0.014), Me, G, 'L15' + sd, seats=('L15' + sd,)) for a in (math.pi / 2, -math.pi / 2): p = Vector((0.138 * math.cos(a), 0.138 * math.sin(a), 0.026)) L.put(rbox((0.010, 0.018, 0.004), 0.001), Me @ Matrix.Translation(p), G, 'L15t' + sd, seats=('L15' + sd,)) def _channel(L, rg): """L04/L05: the back channel between the inner shells: cross rungs on its floor under the barrel; bearing bosses for the trunnion stubs; L16: a guide rail with a threaded rod and a sliding block on the -y wall""" fo = rg['floor_off'] s = -0.32 nb3, u3 = Vector((NB.x, 0, NB.y)), Vector((U2.x, 0, U2.y)) while s <= 0.16: p = T2 + U2 * s + NB * (fo(s) + 0.011 - 0.002) L.put(rbox((0.012, 0.149, 0.022), 0.002), basis((p.x, 0, p.y), nb3, u3), G, 'L05', seats=('L03c', 'L03', 'L02', 'L03f0', 'L03f1', 'L03f2')) s += 0.044 for s_ in (1, -1): L.put(ring(0.031, 0.052, 0.0175, 24, 0.002, G), frame((T2.x, s_ * 0.0752, T2.y), (0, -s_, 0)), G, 'L04b' + ('.L' if s_ > 0 else '.R'), seats=('L03' + ('.L' if s_ > 0 else '.R'),)) # guide rail + threaded rod + sliding block on the -y wall, beside the barrel (|y| 0.056..0.075) pa = T2 + U2 * (-0.25) + NB * (-0.052) pb = T2 + U2 * (-0.075) + NB * (-0.052) rail_len = (pb - pa).length ang = math.atan2(U2.y, U2.x) Mr = Matrix.Translation(((pa.x + pb.x) / 2, 0, (pa.y + pb.y) / 2)) @ Matrix.Rotation(-ang, 4, 'Y') L.put(rbox((rail_len, 0.008, 0.026), 0.0015), Mr @ Matrix.Translation((0, -0.0712, 0)), G, 'L16', seats=('L03.R', 'L04b.R')) rod = thread_rod(0.0055, rail_len - 0.02, 0.011, S, 8) L.put(rod, Mr @ Matrix.Translation((-(rail_len - 0.02) / 2, -0.0610, 0.0)) @ frame((0, 0, 0), 'X'), S, 'L16', seats=('L16',)) L.put(rbox((0.05, 0.016, 0.030), 0.004), Mr @ Matrix.Translation((-0.02, -0.0615, 0)), W, 'L16b', seats=('L16',)) for e in (-1, 1): L.put(rbox((0.010, 0.014, 0.030), 0.0015), Mr @ Matrix.Translation((e * (rail_len / 2 - 0.006), -0.0635, 0)), G, 'L16', seats=('L16',)) def _front_ribs(L, rg): """the dashed strip of ref1/ref2: cross ribs in the dark front recess between the inner shells, near the root""" d = T2.normalized() nf = V2(-d.y, d.x) if nf.dot(V2(0.5, 0.85)) < 0: nf = -nf d3, n3 = Vector((d.x, 0, d.y)), Vector((nf.x, 0, nf.y)) for k in range(12): t = 0.29 + k * 0.026 edge = d * t + nf * (0.20 + 0.05 * t / T2.length) p = edge - nf * 0.0345 L.put(rbox((0.012, 0.148, 0.011), 0.0015), basis((p.x, 0, p.y), n3, d3), G, 'L05f', seats=('L03c', 'L03', 'L03f0', 'L03f1', 'L03f2')) def _handles(L): """L13: bent-tube handles standing off the back of the knee on both sides, a ribbed sleeve on the outer leg""" for s in (1, -1): sd = '.L' if s > 0 else '.R' feet = [] for z in (0.17, 0.48): hit, n = L.snap((-1.6, s * 0.407, z), (1, 0, 0)) feet.append(hit if hit is not None else Vector((-0.80, s * 0.407, z))) xo = min(f.x for f in feet) - 0.150 pts = [(feet[0].x + 0.004, s * 0.407, feet[0].z), (xo, s * 0.407, feet[0].z), (xo, s * 0.407, feet[1].z), (feet[1].x + 0.004, s * 0.407, feet[1].z)] L.put(tube(pts, 0.0132, 12, 0.035, caps=True), None, W, 'L13' + sd, seats=('L04s' + sd, 'L04f' + sd, 'L12' + sd, 'L04r' + sd)) for f in feet: L.put(rbox((0.030, 0.040, 0.040), 0.004), Matrix.Translation((f.x - 0.008, s * 0.407, f.z)), W, 'L13' + sd, seats=('L04s' + sd, 'L04f' + sd, 'L12' + sd, 'L13' + sd, 'L04r' + sd)) zc = (feet[0].z + feet[1].z) / 2 for k in range(5): z = zc - 0.10 + k * 0.05 L.put(lathe([(0.0125, -0.019, 0.0, W), (0.0175, -0.019, 0.0015, W), (0.0175, 0.019, 0.0015, W), (0.0125, 0.019, 0.0, W)], 12, closed=True, n=1), Matrix.Translation((xo, s * 0.407, z)), W, 'L13r' + sd, seats=('L13' + sd,)) def _left_side_details(L): """+y side only: L10 light slot, L11 round button, L08 hexagon, L09 triangle; L17 twin cable gland at the elbow""" # L10 light slot: a raised bezel with a recessed slot along the arm and a pale bar inside c = T2 + KD * 0.62 - KP * 0.13 ang = math.atan2(KD.y, KD.x) Mb = Matrix.Translation((c.x, PANEL_TOP + 0.0005, c.y)) @ Matrix.Rotation(-ang, 4, 'Y') @ basis((0, 0, 0), (0, 1, 0), (1, 0, 0)) bz = rbox((0.33, 0.052, 0.008), 0.002, ops=[('+z', -0.155, 0.155, -0.014, 0.014, -0.006)], rbev=0.0012) L.put(bz, Mb @ Matrix.Translation((0, 0, 0.002)), W, 'L10', seats=('L12.L', 'L04s.L')) L.put(xy_slab(stadium(0.29, 0.016), 0.0, 0.004, 0.0, 0.0012, n=4), Mb @ Matrix.Translation((0, 0, -0.0005)), LA, 'L10l', seats=('L10', 'L12.L', 'L04s.L')) # L11 button p = T2 + KD * 0.36 - KP * 0.17 M = L.on_surface((p.x, 1.0, p.y), (0, -1, 0)) if M is not None: L.put(stack([(0.066, 0.008, 'gunmetal'), (0.048, 0.006, 'paint_white')], 24, base=0.002), M, None, 'L11', seats=('L12.L', 'L04s.L', 'L04r.L')) L.put(xform(lathe([(0.0, 0.0, 0, W), (0.024, 0.0, 0, W), (0.022, 0.006, 0.003, W), (0.0, 0.009, 0, W)], 16), Matrix.Translation((0, 0, 0.014))), M, W, 'L11', seats=('L11',)) # L08 hexagon outline on the inner shell (left), L09 small triangle on the skin (left) M = L.on_surface((-0.176, 1.0, 0.297), (0, -1, 0), up=(0, 0, 1)) if M is not None: hexp = [(0.055 * math.cos(math.radians(30 + 60 * k)), 0.055 * math.sin(math.radians(30 + 60 * k))) for k in range(6)] L.decal(decal_strip(hexp, 0.0066), M, 'L08') p = T2 + KD * 0.40 + KP * 0.05 M = L.on_surface((p.x, 1.0, p.y), (0, -1, 0), up=(0, 0, 1)) if M is not None: tri = [(0.0, 0.022), (-0.019, -0.011), (0.019, -0.011)] L.decal(decal_strip(tri, 0.004), M, 'L09') # L17 twin cable gland on the +y cheek's back edge, pointing back hit, n = L.snap((-1.6, 0.39, 1.17), (1, 0, 0)) if hit is not None: g = lathe([(0.0, -0.01, 0, W), (0.044, -0.01, 0, W), (0.044, 0.018, 0.004, W), (0.0, 0.018, 0, W)], 24) L.put(g, frame(hit, (-1, 0, 0)), W, 'L17', seats=('L14.L', 'L04s.L', 'L12.L', 'L04r.L')) for dy in (-0.020, 0.020): L.put(stack([(0.030, 0.026, 'rubber')], 16, base=0.002), frame(hit + Vector((-0.018, dy, 0)), (-1, 0, 0)), None, 'L17', seats=('L17',)) L.gland_L17 = hit + Vector((-0.044, 0, 0)) # ---------------------------------------------------------------- the J3 cylinder and its piston def _u3(): return Vector((U2.x, 0, U2.y)) def build_cylinder(): """barrel with a blind bore the rod slides in (deep enough for version B's shortest length 0.587 m: the rod's end then reaches 0.213 m behind the trunnion, the bore 0.270), steel gland collar, trunnion collar with two stub pins, hose ports""" L = Link('J3 cylinder') L.base = J2A.copy() Tm = Vector((T2.x, 0, T2.y)) Mu = frame(Tm, _u3()) prof = [(0.0, -0.30, 0, G), (0.034, -0.30, 0.004, G), (0.042, -0.292, 0.004, G), (0.042, 0.455, 0.002, G), (0.047, 0.458, 0.002, S), (0.047, 0.500, 0.003, S), (0.0305, 0.500, 0.001, S), (0.0305, -0.270, 0, G), (0.0, -0.270, 0, G)] L.put(lathe(prof, 32), Mu, None, 'R01') for z in (-0.20, 0.30): L.put(ring(0.0418, 0.0450, 0.012, 32, 0.001, G, z0=z), Mu, G, 'R01', seats=('R01',)) L.put(lathe([(0.0420, -0.035, 0.0, W), (0.054, -0.035, 0.004, W), (0.054, 0.035, 0.004, W), (0.0420, 0.035, 0.0, W)], 32, closed=True), Mu, W, 'R02', seats=('R01',)) for s in (1, -1): L.put(cyl(0.030, 0.040, 24, 0.002, S, z0=0.0345), frame(Tm, (0, s, 0)), S, 'R02p', seats=('R02', 'R01')) for sp in (-0.24, 0.12): p = Tm + _u3() * sp L.put(stack([(0.020, 0.010, 'steel'), (0.014, 0.006, 'steel')], 12, base=0.004), frame(p + Vector((0, 0.0405, 0)), (0, 1, 0)), None, 'R04p', seats=('R01',)) return L def build_piston(): """the dark piston rod (dia 0.058, 0.80 m from its eye back to the piston) with a stepped collar at its end and a thin steel ring, and the rod eye on the knob pin (CRITIQUE_1 item 7)""" L = Link('J3 piston') L.base = J2A.copy() Km = Vector((-1.3354, 0, 0.9394)) # knob in the lower-arm frame at rest Mu = frame(Km, -_u3()) L.put(lathe([(0.0, 0.034, 0, G), (0.0215, 0.034, 0.001, G), (0.0215, 0.044, 0.0, G), (0.034, 0.046, 0.002, G), (0.034, 0.062, 0.002, G), (0.029, 0.064, 0.0, G), (0.029, 0.800, 0.002, G), (0.0, 0.800, 0, G)], 24), Mu, None, 'R05') L.put(ring(0.0289, 0.0312, 0.006, 24, 0.0005, S, z0=0.070), Mu, S, 'R05c', seats=('R05',)) eye = lathe([(0.0255, -0.012, 0.001, S), (0.0375, -0.012, 0.002, S), (0.0375, 0.012, 0.002, S), (0.0255, 0.012, 0.001, S)], 32, closed=True) L.put(eye, frame(Km, 'Y'), S, 'R06', seats=('R05',)) return L # ---------------------------------------------------------------- upper arm (frame: origin J3) KNOB_U = V2(-0.4664, -0.3696) def _truss_outline(): return ccw(list(sd_hull_pts())) def sd_hull_pts(): from dra_lib import hull2 pts = [V2(-0.022, 0.066), V2(0.110, -0.154), V2(-0.132, 0.066)] + circle2(KNOB_U, 0.072, 16) return hull2(pts) def build_upper_arm(): L = Link('upper arm') L.base = rd.J3.copy() # U01 elbow hub between the cheeks L.put(lathe([(0.0, -0.253, 0, G), (0.150, -0.253, 0.006, G), (0.165, -0.240, 0.006, G), (0.165, 0.240, 0.006, G), (0.150, 0.253, 0.006, G), (0.0, 0.253, 0, G)], 48), frame((0, 0, 0), 'Y'), G, 'U01') # U02 housing (the hump), CRITIQUE_1 item 5: the top is one convex curve from the front block over the hump down # into the elbow (ref1 x_img 1100-1560, y_img 468-640), 0.03-0.05 lower than before; the bottom edge as before ol = [(0.925, 0.552, 0.0), (0.800, 0.556, 0.0), (0.650, 0.553, 0.0), (0.520, 0.545, 0.0), (0.400, 0.528, 0.0), (0.300, 0.496, 0.0), (0.200, 0.443, 0.0), (0.110, 0.374, 0.0), (0.030, 0.293, 0.0), (-0.060, 0.192, 0.0), (-0.130, 0.082, 0.0), (-0.185, -0.030, 0.04), (-0.150, -0.090, 0.03), (0.176, -0.066, 0.06), (0.360, 0.040, 0.06), (0.550, 0.100, 0.05), (0.760, 0.128, 0.03), (0.925, 0.138, 0.0)] # stage 3 shape pass: rounded transitions from the sides into the top and the belly (ref1, ref3) L.put(xz_slab(ol, -0.236, 0.236, 0.032, 0.032, steps=3), mat=W, pid='U02', seats=('U01', 'U12', 'U12c')) Po = ccw([p for p, _ in fillet(ol, closed=True, n=3)]) Pi = inset_poly(Po, 0.034) # the dark recessed strip along the lower edge (ref1 x_img 1180-1470, y_img 640-690): what is below the line # z = 0.060 + 0.105 (x - 0.15) / 0.75 (from x 0.0 to the front block) def zl(x): return 0.062 + 0.098 * (x - 0.15) / 0.75 from dra_lib import clip_half as _ch strip = _ch(Po, Vector((0.0, -1.0)), -(-0.10)) # z >= -0.10 strip = _ch(strip, Vector((-1.0, 0.0)), -0.180) # x >= 0.18 (clear of the elbow hub) strip = _ch(strip, Vector((1.0, 0.0)), 0.90) # x <= 0.90 n_l = Vector((-(0.098 / 0.75), 1.0)).normalized() # keep below the line strip = _ch(strip, n_l, n_l.dot(Vector((0.15, 0.062)))) strip = inset_poly(ccw(strip), 0.030) if len(strip) >= 3 else strip # inside the rounded edges for s_ in (1, -1): if len(strip) >= 3: bm = xz_slab([(p.x, p.y, 0.0) for p in strip], 0.226, 0.239, 0.0, 0.002, n=1) if s_ < 0: mirror_y(bm) L.put(bm, mat=G, pid='U02d' + ('.L' if s_ > 0 else '.R'), seats=('U02',)) # the raised side panels (12 mm) above the strip: the big one carries the cap, the back one the vents up = _ch(Pi, -n_l, -n_l.dot(Vector((0.15, 0.062))) - 0.006) # 6 mm above the strip's line up = _ch(up, Vector((-1.0, 0.0)), -0.085) # clear of the elbow's rounded end up = _ch(up, Vector((1.0, 0.0)), 0.900) seams = [((0.300, -0.2), (0.350, 0.7)), ((0.840, -0.2), (0.840, 0.7))] _panels(L, [(p.x, p.y, 0.0) for p in up], 0.248, 0.012, seams, 'U02p', inset=0.0, gap=0.005, bev=0.003, seats=('U02', 'U01', 'U02d')) # a raised spine along the top (ref1: the stepped top line) top = [(0.80, 0.556), (0.650, 0.553), (0.520, 0.545), (0.400, 0.528), (0.300, 0.496), (0.200, 0.443), (0.110, 0.374)] # a raised lid along the top (ref1, ref3: the hump's cover), 10 mm proud, its ends stepped down into the body; # the narrow spine stands on it lid = [(x, z - 0.045) for x, z in top] + [(x, z + 0.010) for x, z in reversed(top)] L.put(xz_slab([(x, z, 0.0) for x, z in lid], -0.180, 0.180, 0.008, 0.008, n=1, steps=3), mat=W, pid='U02l', seats=('U02',)) spine = [(x, z - 0.02) for x, z in top[:-1]] + [(x, z + 0.020) for x, z in reversed(top[:-1])] L.put(xz_slab([(x, z, 0.0) for x, z in spine], -0.055, 0.055, 0.005, 0.005, n=1), mat=W, pid='U02s', seats=('U02', 'U02l')) _upper_side_details(L) _truss(L) _front_block(L) return L def _upper_side_details(L): for s in (1, -1): sd = '.L' if s > 0 else '.R' # U03 big caps: white bezel ring, thin orange ring in its bore, a dark glass disc set back, a slotted steel head Mc = frame((0.634, s * 0.247, 0.341), (0, s, 0)) bez = lathe([(0.116, 0.0, 0.0, W), (0.181, 0.0, 0.006, W), (0.181, 0.031, 0.010, W), (0.125, 0.031, 0.004, W), (0.116, 0.024, 0.0, W)], 40, closed=True) L.put(bez, Mc, None, 'U03' + sd, seats=('U02p' + sd, 'U02')) L.put(ring(0.110, 0.1162, 0.022, 40, 0.0, O), Mc, O, 'U03' + sd, seats=('U03' + sd,)) L.put(lathe([(0.0, 0.0006, 0, GL), (0.1104, 0.0006, 0, GL), (0.1104, 0.012, 0.002, GL), (0.0, 0.012, 0, GL)], 40), Mc, GL, 'U03g' + sd, seats=('U03' + sd, 'U02p' + sd)) L.put(cyl(0.079, 0.006, 32, 0.001, S, z0=0.012), Mc, S, 'U03h' + sd, seats=('U03g' + sd,)) L.put(xy_slab(stadium(0.150, 0.030), 0.018, 0.028, 0.0, 0.002, n=3), Mc, S, 'U03h' + sd, seats=('U03h' + sd,)) # U04 ribbed vents along the top edge (inclined like the edge): a raised plate with 8 slots ang = math.atan2(0.154, 0.286) Mv = Matrix.Translation((0.180, s * 0.248, 0.300)) @ Matrix.Rotation(-ang, 4, 'Y') L.put(xz_slab(rrect(0.22, 0.121, 0.012), -0.004, 0.004, 0.0, 0.002) if False else rbox((0.22, 0.008, 0.121), 0.003), Mv @ Matrix.Translation((0, s * 0.002, 0)), W, 'U04' + sd, seats=('U02p' + sd,)) for k in range(9): x = -0.096 + k * 0.024 L.put(rbox((0.012, 0.010, 0.099), 0.002, segs=1), Mv @ Matrix.Translation((x, s * 0.006, 0)), W, 'U04' + sd, seats=('U04' + sd,)) # U05 indicator light, U06 small dot (left), U07 chevrons (left) M = frame((0.429, s * 0.248, 0.200), (0, s, 0)) L.put(stack([(0.073, 0.006, 'gunmetal'), (0.055, 0.005, 'lamp')], 24, base=0.002), M, None, 'U05' + sd, seats=('U02p' + sd,)) L.put(stack([(0.018, 0.004, 'gunmetal')], 12, base=0.002), frame((0.297, 0.248, 0.250), 'Y'), None, 'U06', seats=('U02p.L',)) M = L.on_surface((0.080, 1.0, 0.380), (0, -1, 0), up=(0, 0, 1)) if M is not None: bm = decal_strip([(-0.014, -0.010), (0.0, 0.004), (0.014, -0.010)], 0.0045, closed=False) b2 = decal_strip([(-0.014, 0.004), (0.0, 0.018), (0.014, 0.004)], 0.0045, closed=False) merge(bm, b2) b2.free() L.decal(bm, M, 'U07') # U08 twin cable gland on the top behind the vents, two rubber glands facing back; on it the short thick looped # hose of ref1 (x_img 1377-1463, y_img 480-560) and ref3 (x 630-730, y 450-530) hit, n = L.snap((0.24, 0.110, 1.5), (0, 0, -1)) if hit is not None: L.put(rbox((0.11, 0.088, 0.100), 0.010), Matrix.Translation(hit + Vector((0, 0, 0.005))), W, 'U08', seats=('U02', 'U02s', 'U02p.L', 'U02l')) for dy in (-0.020, 0.020): L.put(stack([(0.0352, 0.022, 'rubber'), (0.026, 0.006, 'rubber')], 16, base=0.002), frame(hit + Vector((-0.053, dy, 0.030)), (-1, 0, 0)), None, 'U08g', seats=('U08',)) L.gland_U08 = hit + Vector((-0.083, 0, 0.030)) top = hit.z + 0.055 for xf in (0.030, -0.034): L.put(stack([(0.044, 0.010, 'paint_white'), (0.036, 0.012, 'rubber')], 16, base=0.002), frame((hit.x + xf, hit.y, top), 'Z'), None, 'U08h', seats=('U08',)) loop = [(hit.x + 0.030, hit.y, top + 0.018), (hit.x + 0.040, hit.y, top + 0.085), (hit.x - 0.060, hit.y, top + 0.105), (hit.x - 0.070, hit.y, top + 0.050), (hit.x - 0.034, hit.y, top + 0.018)] L.put(tube(loop, 0.0155, 12, 0.035, caps=True), None, R, 'U08l', seats=('U08h',)) # indicator rectangle (lamp) and dark bolt near the front block, left side L.put(rbox((0.036, 0.006, 0.020), 0.002), Matrix.Translation((0.870, 0.2485, 0.430)), LA, 'U12i', seats=('U02p.L',)) L.put(hexbolt(0.020, 0.008), frame((0.860, 0.2385, 0.128), 'Y'), None, 'U12d', seats=('U02d.L',)) def _truss(L): """U10 truss plates (|y| 0.052..0.090; stage 2: the inner faces moved out from 0.040 so the J3 barrel (r 0.047 at its gland) passes between the plates when the elbow folds for version B, J3 up to 45 deg), CRITIQUE_1 item 7: a white lever about 0.15 m wide (ref1 x_img 1600-1795, y_img 720-890; ref5) with a raised rim and five recessed pockets with white floors on each outer face: a slot and four triangles in a zigzag (the 'ANN' of the pictures). U11 knob: the pin through both plates and the rod eye, on each side a finely knurled ring round a domed boss.""" A0 = V2(-0.070, -0.035) # the lever's elbow end, behind the elbow cap K_ = KNOB_U from dra_lib import hull2 H = hull2([A0 + V2(math.cos(a), math.sin(a)) * 0.078 for a in [TAU * k / 32 for k in range(32)]] + [K_ + V2(math.cos(a), math.sin(a)) * 0.070 for a in [TAU * k / 32 for k in range(32)]]) H = ccw(H) d = (K_ - A0).normalized() nrm = V2(-d.y, d.x) pockets = [] sl = [A0 + d * (0.120 + x) + nrm * y for (x, y) in stadium(0.075, 0.032, 0.0, 0.0, 0.0, n=4)] pockets.append([(p.x, p.y, 0.0) for p in ccw(sl)]) w = 0.046 for k in range(4): t0 = 0.180 + k * 0.064 t1 = t0 + 0.090 if k % 2 == 0: tri = [A0 + d * t0 - nrm * w, A0 + d * t1 - nrm * w, A0 + d * ((t0 + t1) / 2) + nrm * w] else: tri = [A0 + d * t0 + nrm * w, A0 + d * ((t0 + t1) / 2) - nrm * w, A0 + d * t1 + nrm * w] cc = sum(tri, V2(0, 0)) / 3 tri = [cc + (p - cc) * 0.78 for p in tri] pockets.append([(p.x, p.y, 0.006) for p in ccw(tri)]) outline = [(p.x, p.y, 0.0) for p in H] land = [(p.x, p.y, 0.0) for p in inset_poly(H, 0.0135)] hole = [(p.x, p.y, 0.0) for p in inset_poly(H, 0.013)] for s in (1, -1): sd = '.L' if s > 0 else '.R' parts = ((xz_slab(outline, 0.052, 0.074, 0.003, 0.0, n=1), 'U10'), (xz_slab(outline, 0.072, 0.090, 0.0, 0.003, n=1, holes=[hole]), 'U10r'), (xz_slab(land, 0.072, 0.0855, 0.0, 0.002, n=1, holes=pockets), 'U10l')) for bm, pid in parts: if s < 0: mirror_y(bm) L.put(bm, mat=W, pid=pid + sd, seats=('U01', 'U02', 'U10' + sd, 'U10r' + sd, 'U10l' + sd)) Mk = frame((K_.x, s * 0.0855, K_.y), (0, s, 0)) L.put(knurl(0.0585, 0.0625, 0.034, 36, 0.003, S), Mk, None, 'U11' + sd, seats=('U10' + sd, 'U10r' + sd, 'U10l' + sd)) L.put(lathe([(0.0, 0.030, 0, G), (0.052, 0.030, 0.002, G), (0.050, 0.036, 0.0, G), (0.040, 0.036, 0.0, G), (0.038, 0.040, 0.0, S), (0.030, 0.046, 0.0, S), (0.018, 0.050, 0.0, S), (0.0, 0.0515, 0, S)], 32), Mk, None, 'U11d' + sd, seats=('U11' + sd,)) L.put(cyl(0.023, 0.180, 24, 0.001, S, z0=-0.090), frame((K_.x, 0, K_.y), 'Y'), S, 'U11p', seats=('U10.L', 'U10.R', 'U11.L', 'U11.R', 'U10r.L', 'U10r.R', 'U10l.L', 'U10l.R', 'U11d.L', 'U11d.R')) # spacers on the pin between the plates and the rod eye (the plates stand 0.052 off the middle so the rod's end # collar passes between them) for s in (1, -1): L.put(ring(0.0228, 0.031, 0.0375, 24, 0.001, S, z0=0.0145), frame((K_.x, 0, K_.y), (0, s, 0)), S, 'U11w', seats=('U11p', 'U10.L', 'U10.R')) def _front_block(L): """U12 front block (J4 housing): four slices with three ring grooves, on a core; square button on the left""" x0, x1 = 0.9035, 1.1125 zc, hz = 0.356, 0.451 L.put(rbox((x1 - x0 - 0.01, 0.450, hz - 0.012), 0.010), Matrix.Translation(((x0 + x1) / 2, 0, zc)), W, 'U12c', seats=('U02',)) cuts = [x0, 0.955, 0.959, 1.010, 1.014, 1.065, 1.069, x1] for k in range(0, len(cuts), 2): a, b = cuts[k], cuts[k + 1] L.put(rbox((b - a, 0.462, hz), 0.004, rv=0.0264, vax=0, segs=2, segs_v=3), Matrix.Translation(((a + b) / 2, 0, zc)), W, 'U12', seats=('U12c', 'U02')) L.put(rbox((0.026, 0.008, 0.026), 0.003), Matrix.Translation((0.982, 0.233, 0.44)), G, 'U12b', seats=('U12',)) # ---------------------------------------------------------------- curves: the cables over the elbow, the cylinder hoses def _curve(name, pts, radius, coll, parent, mat): cu = bpy.data.curves.get(name) or bpy.data.curves.new(name, 'CURVE') cu.splines.clear() cu.dimensions = '3D' cu.bevel_mode = 'ROUND' cu.bevel_depth = radius cu.bevel_resolution = 2 cu.resolution_u = 8 cu.use_fill_caps = True sp = cu.splines.new('BEZIER') sp.bezier_points.add(len(pts) - 1) for bp, p in zip(sp.bezier_points, pts): bp.co = p bp.handle_left_type = bp.handle_right_type = 'AUTO' cu.materials.clear() cu.materials.append(bpy.data.materials[mat]) ob = bpy.data.objects.new(name, cu) coll.objects.link(ob) ob.parent = parent return ob import bpy # noqa: E402 def add_curves(coll, prefix, objs): """elbow cable pair (U09) from the upper arm's twin gland over the elbow to the gland at the back of the elbow, and the two cylinder hoses; at rest, parented to the lower arm (the rig stage hooks their ends)""" la = objs.get("lower arm") if la is None or "upper arm" not in objs: return J3l = Vector((J3L.x, 0, J3L.y)) me_u = objs["upper arm"].data me_l = la.data if "gland_U08" not in me_u or "gland_L17" not in me_l: return gu = Vector(me_u["gland_U08"]) + (rd.J3 - rd.J2) # in the lower-arm frame gl = Vector(me_l["gland_L17"]) # CRITIQUE_1 item 9: two loops of different size that cross once in a side view (ref1, ref3): the first leaves the # gland low and makes the big loop, the second leaves steeply and makes the small loop inside it paths = ([gu + Vector((0, -0.020, 0)), gu + Vector((-0.13, -0.020, 0.07)), J3l + Vector((-0.18, 0.16, 0.62)), J3l + Vector((-0.52, 0.27, 0.36)), gl + Vector((-0.15, -0.020, 0.08)), gl + Vector((0, -0.020, 0))], [gu + Vector((0, 0.020, 0)), gu + Vector((-0.05, 0.020, 0.13)), J3l + Vector((-0.30, 0.21, 0.44)), J3l + Vector((-0.42, 0.31, 0.18)), gl + Vector((-0.09, 0.020, -0.03)), gl + Vector((0, 0.020, 0))]) for k, pts in enumerate(paths): _curve("%s | cable elbow %d" % (prefix, k + 1), pts, 0.013, coll, la, "M_DoorArm_Rubber") Tm = Vector((T2.x, 0, T2.y)) for nm, sp in (("rear", -0.24), ("front", 0.12)): p0 = Tm + _u3() * sp + Vector((0, 0.052, 0)) p1 = p0 + Vector((0, 0.008, 0)) + Vector((NB.x, 0, NB.y)) * -0.03 p2 = Tm + _u3() * (sp + 0.07) + Vector((NB.x, 0, NB.y)) * -0.060 + Vector((0, 0.0735, 0)) _curve("%s | J3 hose %s" % (prefix, nm), [p0, p1, p2], 0.010, coll, la, "M_DoorArm_Rubber")