"""Door robot arm: the carriage, its pinion and the turret (DESIGN.md section 11, parts C.. and T..). Carriage frame = arm frame A at rest: X along the rail, Y across, Z up from the rail to the turntable face (Z = 0). In the ship the arm hangs, so carriage Z points DOWN there: ship = (c + X, -5.795 - Y, -4.182 - Z). Changes to the design, made here (reasons in STATUS.md): - the rail channel goes up to the top plate (Z -0.044), so the drive pinion fits above the rack without a pocket; - the drive motor lies along Y beside the pinion under the top plate (the design's motor dia 0.12 did not fit); - the side guide rollers run on the rail's own side faces (between rail and cheek), not inside the cheeks; - the strike pads stand 5 mm proud of the end faces (10 mm short of the buffers became 5 mm); - the pinion is its own object (it has to turn with the carriage's travel to stay in mesh with the rack). """ 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, W, G, S, R, O, GL, LA, TAU, K, circle2, decal_strip, decal_poly, decal_dot, fillet3d) from dra_shapes import (rrect, stadium, xz_slab, yz_slab, xy_slab, screw, dome, hexbolt, ring, cyl, grooved_cyl, holed_plate, gear_outline, notched_disc, half_discs, quarter_discs, u_handle, M_XZ) from dra_lib import stack as rings_stack # ---------------------------------------------------------------- carriage numbers HA = 0.429 # half size in plan CH_IN = 0.289 # cheek inner face (channel half width) TOP_Z = -0.044 # underside of the top plate = channel ceiling PIN_Z = -0.100 # pinion axis PIN_MOD = 0.008 # pinion module (10 teeth, pitch radius 0.040) PIN_TEETH = 10 PIN_RP = PIN_MOD * PIN_TEETH / 2 RACK_PITCH = math.pi * PIN_MOD def _plates_cheek(L, s): """one cheek (side s = +1 / -1): the five stacked plates, strap band, lower block, skirt and feet""" def put(bm, pid, mat=G, seats=()): if s < 0: mirror_y(bm) L.put(bm, mat=mat, pid=pid + ('.L' if s > 0 else '.R'), seats=seats) def outline(a, yo, ro=0.053, ri=0.006): return [(-a, CH_IN, ri), (a, CH_IN, ri), (a, yo, ro), (-a, yo, ro)] # core behind the V-grooves put(xy_slab(outline(HA - 0.008, HA - 0.008, 0.045), -0.374, -0.0455, 0.002, 0.002), 'C02core', seats=('C01', 'C02', 'C03', 'C04', 'C05')) for k in range(5): zt = TOP_Z - 0.0005 - k * 0.0341 zb = zt - 0.0286 a = HA - 0.0045 - (0.0022 if k % 2 else 0.0) put(xy_slab(outline(a, a), zb, zt, 0.0042, 0.0042, steps=1), 'C02', seats=('C02core', 'C01')) put(xy_slab(outline(0.4312, 0.4312, 0.0484), -0.297, -0.2115, 0.004, 0.0045), 'C03', seats=('C02core',)) put(xy_slab(outline(0.4268, 0.4268, 0.050), -0.3065, -0.2995, 0.0016, 0.0016), 'C04', seats=('C02core',)) put(xy_slab(outline(0.4268, 0.4268, 0.050), -0.363, -0.309, 0.003, 0.0016), 'C04', seats=('C02core',)) put(xy_slab(outline(HA, HA, 0.055), -0.3740, -0.3655, 0.002, 0.003), 'C05', seats=('C02core', 'C04')) for sx in (1, -1): prof = [(0.203, -0.374, 0.0), (HA - 0.001, -0.374, 0.0), (HA - 0.001, -0.407, 0.006), (0.236, -0.407, 0.004)] if sx < 0: prof = [(-x, z, r) for x, z, r in reversed(prof)] put(xz_slab(prof, CH_IN + 0.002, HA - 0.004, 0.003, 0.004), 'C05f', seats=('C05', 'C02core')) # thin bead on the lower block face put(xz_slab(rrect(0.78, 0.004, 0.0015, 0, -0.336), 0.4262, 0.4300, 0.0, 0.0012), 'C04b', seats=('C04',)) def _band_details(L, s): """dot panels, straps and the buckle chain on one outer face (the rail-side faces in the ship)""" yf = 0.4312 * s def put(bm, pid, mat=None, seats=()): if s < 0: mirror_y(bm) L.put(bm, mat=mat, pid=pid + ('.L' if s > 0 else '.R'), seats=seats) for sx in (1, -1): p = holed_plate(0.154, 0.072, 0.0072, 3, 2, 0.0286, 0.0075, 0.0045, mat=G) put(xform(p, basis((sx * 0.330, 0.4302, -0.253), (0, 1, 0), (1, 0, 0))), 'C03a', G, seats=('C03',)) # strap put(xz_slab(rrect(0.100, 0.0308, 0.006, sx * 0.198, -0.246), 0.4302, 0.4392, 0.0, 0.002), 'C03b', G, seats=('C03', 'C03c')) # buckle chain (CRITIQUE_1 item 10; ref4 x 730-1340, y 1480-1560, ref7, ref1): five flat rectangular plates linked # by pins with round bosses, the outer pairs angled down toward the middle plate, which hangs lowest cx = [-0.178, -0.090, 0.0, 0.090, 0.178] cz = [-0.247, -0.257, -0.266, -0.257, -0.247] ang = [math.radians(-11), math.radians(-11), 0.0, math.radians(11), math.radians(11)] for i in range(5): y0 = 0.4305 + (0.0030 if i % 2 else 0.0) ca, sa = math.cos(ang[i]), math.sin(ang[i]) rect = [(-0.042, -0.016), (0.042, -0.016), (0.042, 0.016), (-0.042, 0.016)] outline = [(cx[i] + x * ca - z * sa, cz[i] + x * sa + z * ca, 0.004) for x, z in rect] put(xz_slab(outline, y0, y0 + 0.0090, 0.0, 0.0018, n=1, steps=1), 'C03c', G, seats=('C03', 'C03b', 'C03c')) # a raised rib along the plate and a slot in the middle plate rib = [(-0.030, -0.004), (0.030, -0.004), (0.030, 0.004), (-0.030, 0.004)] if i != 2: put(xz_slab([(cx[i] + x * ca - z * sa, cz[i] + x * sa + z * ca, 0.002) for x, z in rib], y0 + 0.0085, y0 + 0.0115, 0.0, 0.001, n=1, steps=1), 'C03c', G, seats=('C03c',)) # pin bosses where the plates overlap (between neighbours), standing out of the outer plate for i in range(4): px = (cx[i] + cx[i + 1]) / 2 pz = (cz[i] + cz[i + 1]) / 2 put(xform(cyl(0.0085, 0.0140, 12, 0.0012, S), frame((px, 0.4325, pz), 'Y')), 'C03c', G, seats=('C03c', 'C03')) put(xform(cyl(0.0040, 0.0035, 8, 0.0006, G), frame((px, 0.4465, pz), 'Y')), 'C03c', G, seats=('C03c',)) # middle latch plate of the buckle put(xz_slab(rrect(0.05, 0.022, 0.004, 0.0, -0.266), 0.4395, 0.4430, 0.0, 0.0012), 'C03c', G, seats=('C03c',)) def _running_gear(L): """wheels, counter-rollers, side guide rollers, drive motor, strike pads, chain bracket""" for s in (1, -1): def put(bm, pid, mat=None, seats=()): if s < 0: mirror_y(bm) L.put(bm, mat=mat, pid=pid + ('.L' if s > 0 else '.R'), seats=seats) for x in (0.30, -0.30): # load wheel (rolls on the running flange's top): tyre, hub, axle into the cheek, boss on the cheek face prof = [(0.0, 0.2365, 0, S), (0.044, 0.2365, 0.002, S), (0.0495, 0.2385, 0.002, S), (0.0495, 0.2695, 0.002, S), (0.044, 0.2715, 0.002, S), (0.020, 0.2715, 0, S), (0.020, 0.274, 0, S), (0.0, 0.274, 0, S)] put(xform(lathe(prof, 20, n=1), Matrix.Translation((x, 0, -0.252)) @ frame((0, 0, 0), 'Y')), 'C07', S, seats=()) put(xform(cyl(0.010, 0.020, 8, 0.0, S), frame((x, 0.273, -0.252), 'Y')), 'C07ax', S, seats=('C07', 'C02core', 'C03', 'C02')) put(xform(ring(0.0105, 0.022, 0.006, 12, 0.0, G), frame((x, 0.283, -0.252), 'Y')), 'C07ax', G, seats=('C03', 'C02', 'C02core')) # counter roller (under the running flange) prof = [(0.0, 0.2365, 0, S), (0.0255, 0.2365, 0.002, S), (0.0295, 0.239, 0.002, S), (0.0295, 0.269, 0.002, S), (0.0255, 0.2715, 0.002, S), (0.0, 0.2715, 0, S)] put(xform(lathe(prof, 16, n=1), Matrix.Translation((x, 0, -0.142)) @ frame((0, 0, 0), 'Y')), 'C08', S) put(xform(cyl(0.008, 0.022, 8, 0.0, S), frame((x, 0.2705, -0.142), 'Y')), 'C08ax', S, seats=('C08', 'C02', 'C02core')) put(xform(ring(0.0085, 0.018, 0.005, 12, 0.0, G), frame((x, 0.284, -0.142), 'Y')), 'C08ax', G, seats=('C02', 'C02core')) for x in (0.15, -0.15): # side guide roller on a vertical axle, running on the rail's side face (|Y| 0.229) prof = [(0.0, -0.360, 0, S), (0.021, -0.360, 0.002, S), (0.0245, -0.357, 0.002, S), (0.0245, -0.333, 0.002, S), (0.021, -0.330, 0.002, S), (0.0, -0.330, 0, S)] bm = lathe(prof, 16, n=1) xform(bm, Matrix.Translation((x, 0.254, 0))) put(bm, 'C09', S) for z0 in (-0.3285, -0.3675): put(xform(rbox((0.044, 0.060, 0.006), 0.0015, segs=1), Matrix.Translation((x, 0.263, z0 + 0.003))), 'C09b', G, seats=('C02', 'C02core', 'C03', 'C04', 'C09p', 'C05', 'C05f')) put(xform(cyl(0.006, 0.050, 8, 0.0, S), frame((x, 0.254, -0.3705), 'Z')), 'C09p', S, seats=('C09b', 'C09')) # a web joining the two bracket plates to the cheek put(yz_slab(rrect(0.012, 0.045, 0.002, 0.286, -0.345), x - 0.022, x + 0.022, 0.001, 0.001), 'C09b', G, seats=('C02', 'C02core', 'C03', 'C04', 'C05', 'C05f')) # drive: mount plate under the top plate, gearbox, motor (along +Y), bearing block; pinion is its own object for (y0, y1) in ((0.026, 0.075), (-0.059, -0.026)): L.put(xy_slab(rrect(0.12, y1 - y0, 0.006, 0.0, (y0 + y1) / 2), TOP_Z - 0.010, TOP_Z - 0.0005, 0.002, 0.001), mat=G, pid='C10m', seats=('C01',)) L.put(rbox((0.11, 0.046, 0.072), 0.004), Matrix.Translation((0.0, 0.049, -0.090)), G, 'C10g', seats=('C10m',)) mot = grooved_cyl(0.040, 0.160, [0.03, 0.05, 0.07, 0.09, 0.11, 0.13], depth=0.004, width=0.008, segs=20, f=0.004, mat=G) L.put(mot, Matrix.Translation((0, 0.072, PIN_Z)) @ frame((0, 0, 0), 'Y'), None, 'C10mot', seats=('C10g',)) cap = rings_stack([(0.080, 0.006, 'steel'), (0.064, 0.002, 'orange'), (0.056, 0.008, 'gunmetal')], 20) L.put(cap, Matrix.Translation((0, 0.232, PIN_Z)) @ frame((0, 0, 0), 'Y'), None, 'C10cap', seats=('C10mot',)) for k in range(6): a = TAU * k / 6 L.put(screw(0.0035), Matrix.Translation((0.033 * math.cos(a), 0.238, PIN_Z + 0.033 * math.sin(a))) @ frame((0, 0, 0), 'Y'), None, 'C10cap', seats=('C10cap',)) L.put(rbox((0.07, 0.030, 0.068), 0.004), Matrix.Translation((0.0, -0.041, -0.088)), G, 'C10b', seats=('C10m',)) # strike pads (both end faces), hung from the top plate above the rack for sx in (1, -1): L.put(rbox((0.05, 0.12, 0.080), 0.004), Matrix.Translation((sx * 0.393, 0, -0.084)), G, 'C12h', seats=('C01',)) L.put(rbox((0.020, 0.20, 0.066), 0.003), Matrix.Translation((sx * 0.424, 0, -0.093)), S, 'C12', seats=('C12h',)) for yy in (-0.07, 0.07): L.put(screw(0.006, h=0.0012), Matrix.Translation((sx * 0.4335, yy, -0.093)) @ frame((0, 0, 0), (sx, 0, 0)), None, 'C12', seats=('C12',)) # countersunk: the buffers meet this face # energy-chain bracket on the inboard (-Y) cheek: plate on the cheek, arm over the tray wall, drop plate to the chain L.put(xz_slab(rrect(0.10, 0.12, 0.006, 0.0, -0.360), -0.4405, -0.4275, 0.002, 0.001), mat=S, pid='C11', seats=('C04.R', 'C05.R', 'C03.R', 'C04b.R')) L.put(xy_slab(rrect(0.10, 0.176, 0.008, 0.0, -0.5145), -0.4205, -0.4085, 0.0015, 0.0015), mat=S, pid='C11', seats=('C11',)) # the drop plate stands on the carriage's middle (x 0); the chain's end link is pinned 7.5 mm beside it (AFT on the # -x side, FORE on the +x side: the rig shifts the chain's moving end by 0.020) L.put(yz_slab(rrect(0.050, 0.116, 0.006, -0.565, -0.358), -0.006, 0.006, 0.0015, 0.0015), mat=S, pid='C11', seats=('C11',)) L.put(cyl(0.008, 0.014, 12, 0.001, S, z0=-0.007), Matrix.Translation((0, -0.565, -0.330)) @ frame((0, 0, 0), "X"), S, "C11", seats=('C11',)) def build_carriage(): L = Link('carriage') # C01 top plate + raised pad + fixed race of the slewing ring with its bolts L.put(xy_slab(rrect(0.858, 0.858, 0.055), TOP_Z, 0.0, 0.004, 0.0066), mat=G, pid='C01') L.put(xy_slab(rrect(0.726, 0.726, 0.05), -0.001, 0.008, 0.0, 0.0025), mat=G, pid='C01p', seats=('C01',)) L.put(lathe([(0.250, 0.0075, 0.0, G), (0.297, 0.0075, 0.002, G), (0.297, 0.026, 0.002, G), (0.250, 0.026, 0.0015, G)], 48, closed=True), mat=G, pid='C13', seats=('C01p',)) for k in range(24): a = TAU * (k + 0.5) / 24 L.put(screw(0.0044, 0.0055, segs=6), Matrix.Translation((0.2805 * math.cos(a), 0.2805 * math.sin(a), 0.026)), None, 'C13b', seats=('C13',)) for s in (1, -1): _plates_cheek(L, s) _band_details(L, s) _running_gear(L) return L def build_pinion(): """the drive pinion: 10 teeth, module 8, a tooth pointing straight at the rack (-Z) at rest; axis along Y""" L = Link('carriage pinion') out = gear_outline(PIN_TEETH, PIN_RP - 1.25 * PIN_MOD, PIN_RP + PIN_MOD, 0.30, 0.13, phase=-math.pi / 2) g = star_slab(out, -0.020, 0.020, hub_r=0.016, bev=0.0012, mat=S) # local XY plane of the gear -> X-Z plane of the carriage (axis along Y): local x -> X, local y -> Z, local z -> -Y L.put(g, Matrix.Translation((0, 0, PIN_Z)) @ M_XZ, S, 'C10p') L.put(cyl(0.012, 0.050, 16, 0.001, S, z0=-0.025), Matrix.Translation((0, 0, PIN_Z)) @ frame((0, 0, 0), 'Y'), S, 'C10p', seats=('C10p',)) return L # ---------------------------------------------------------------- turret def _housing_outline(): return [(0.55, 0.242, 0.012), (0.55, 0.638, 0.010), (0.44, 0.748, 0.020), (0.132, 0.770, 0.04), (-0.080, 0.748, 0.03), (-0.080, 0.242, 0.006)] def _side_panels(L, outline, half_w, depth, seams, pid, inset=0.007, gap=0.0035, bev=0.0025, mat=W, seats=()): """raised panels on both side faces of an X-Z shell: the outline inset, cut by seam lines (pairs of points)""" from dra_lib import fillet as _f, ccw as _ccw, inset_poly, split_poly P = _ccw([p for p, _ in _f(outline, closed=True, n=3)]) 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)))) > 1e-5: nxt.append(q) pieces = nxt for s in (1, -1): for pc in pieces: o = [(p.x, p.y, 0.0) for p in pc] bm = xz_slab(o, half_w - depth - 0.002, half_w, 0.0, bev, n=2) if s < 0: mirror_y(bm) L.put(bm, mat=mat, pid=pid + ('.L' if s > 0 else '.R'), seats=seats) return pieces def build_turret(): L = Link('turret') # T01 turning race: an inner ring inside the fixed race, three stacked rings with two V grooves above it prof = [(0.0, 0.010, 0, G), (0.246, 0.010, 0.002, G), (0.246, 0.036, 0.001, G), (0.288, 0.038, 0.002, G), (0.288, 0.0425, 0, G), (0.2845, 0.0450, 0, G), (0.288, 0.0475, 0, G), (0.288, 0.0505, 0, G), (0.2845, 0.0530, 0, G), (0.288, 0.0555, 0, G), (0.288, 0.059, 0.001, G), (0.0, 0.059, 0, G)] L.put(lathe(prof, 48), None, None, 'T01') # T02 lower block: two plan slabs with a groove between them, on a core blk = rrect(0.77, 0.726, 0.044, -0.033, 0.0) L.put(xy_slab(rrect(0.758, 0.714, 0.04, -0.033, 0.0), 0.06, 0.236, 0.002, 0.002), mat=W, pid='T02core', seats=('T01',)) L.put(xy_slab(blk, 0.0555, 0.1185, 0.004, 0.003), mat=W, pid='T02', seats=('T01', 'T02core')) L.put(xy_slab(blk, 0.1215, 0.2405, 0.003, 0.006), mat=W, pid='T02', seats=('T02core',)) # T03 front housing: core across Y with rounded rims, raised side panels with seams ho = _housing_outline() L.put(xz_slab(ho, -0.300, 0.300, 0.012, 0.012), mat=W, pid='T03', seats=('T02',)) _side_panels(L, ho, 0.308, 0.008, [((-0.2, 0.530), (0.7, 0.530)), ((0.30, 0.82), (0.62, 0.50)), ((0.30, 0.0), (0.30, 0.530))], 'T03p', seats=('T03',)) lip = [(-0.086, 0.236, 0.0), (0.562, 0.236, 0.0), (0.562, 0.262, 0.010), (-0.086, 0.262, 0.010)] L.put(xz_slab(lip, -0.316, 0.316, 0.006, 0.006), mat=W, pid='T03b', seats=('T03', 'T02', 'T03p.L', 'T03p.R', 'T11', 'T04c.L', 'T04c.R', 'T04.L', 'T04.R')) # lid on top (follows the roof line) and its plug lid = [(0.0, 0.7618, 0.0), (0.132, 0.768, 0.0), (0.286, 0.7576, 0.0), (0.286, 0.7710, 0.004), (0.132, 0.7812, 0.006), (0.0, 0.7750, 0.004)] L.put(xz_slab(lid, -0.25, 0.25, 0.006, 0.006), mat=W, pid='T03l', seats=('T03',)) L.put(rings_stack([(0.055, 0.020, 'paint_white'), (0.036, 0.006, 'gunmetal')], 24), frame((0.330, 0, 0.7735)), None, 'T03a', seats=('T03l', 'T03')) # T04 J2 ears. Each: a white collar round the drum (the white ring of ref1, fixed to the turret; its bore 0.252 > # the drum's 0.245, its outer radius 0.36 < the 0.40 where the lower arm widens) and a rounded motor pod outside it, # with the motor cap stack (T04a) on its outer face. Smaller than the design's 0.572 m box: the lower arm's wide # shells swing past the pods (they stay inside 0.27 m of J2). J2 = Vector((-0.341, 0.0, 0.517)) for s in (1, -1): sd = '.L' if s > 0 else '.R' col = lathe([(0.252, 0.0, 0.002, W), (0.302, 0.0, 0.010, W), (0.302, 0.044, 0.012, W), (0.252, 0.044, 0.003, W)], 48, closed=True) L.put(col, frame((J2.x, s * 0.246, J2.z), (0, s, 0)), W, 'T04c' + sd, seats=('T02', 'T03', 'T04' + sd, 'T02core')) pod = rbox((0.34, 0.1815, 0.46), 0.012, rv=0.15, vax=1, segs=2, segs_v=4) L.put(pod, Matrix.Translation((J2.x, s * 0.3808, J2.z - 0.030)), W, 'T04' + sd, seats=('T02', 'T03', 'T04c' + sd)) cap = rings_stack([(0.240, 0.026, 'paint_white'), (0.190, 0.016, 'gunmetal'), (0.124, 0.005, 'orange'), (0.112, 0.008, 'gunmetal'), (0.100, 0.008, 'gunmetal'), (0.088, 0.008, 'gunmetal')], 32, base=0.004) L.put(cap, frame((J2.x, s * 0.4715, J2.z), (0, s, 0)), None, 'T04a' + sd, seats=('T04' + sd,)) L.put(hexbolt(0.036, 0.016), frame((J2.x, s * (0.4715 + 0.071), J2.z), (0, s, 0)), None, 'T04b' + sd, seats=('T04a' + sd,)) for k in range(8): a = TAU * (k + 0.5) / 8 pp = Vector((J2.x + 0.107 * math.cos(a), s * (0.4715 + 0.026), J2.z + 0.107 * math.sin(a))) L.put(screw(0.0055), frame(pp, (0, s, 0)), None, 'T04s' + sd, seats=('T04a' + sd,)) # T05 connector boss (left), T06 cable port glands (both) L.put(rings_stack([(0.114, 0.022, 'paint_white'), (0.086, 0.006, 'gunmetal'), (0.066, 0.018, 'gunmetal'), (0.030, 0.006, 'steel')], 24, base=0.003), frame((0.407, 0.308, 0.440), 'Y'), None, 'T05', seats=('T03p.L',)) for s in (1, -1): L.put(rings_stack([(0.106, 0.026, 'paint_white'), (0.080, 0.006, 'paint_white'), (0.070, 0.044, 'rubber')], 24, base=0.003), frame((0.187, s * 0.308, 0.440), (0, s, 0)), None, 'T06' + ('.L' if s > 0 else '.R'), seats=('T03p.L' if s > 0 else 'T03p.R',)) # T16 dot vent and T17 recessed vent slots vent = holed_plate(0.088, 0.055, 0.004, 6, 4, 0.0122, 0.0032, 0.003, mat=G) L.put(vent, frame((0.20, 0.3628, 0.185), 'Y'), None, 'T16', seats=('T02',)) for s in (1, -1): sl = rbox((0.050, 0.110, 0.006), 0.0015, ops=[('+z', -0.012, 0.012, -0.046, 0.046, -0.004)], rbev=0.001) L.put(sl, frame((0.470, s * 0.3075, 0.32), (0, s, 0), spin=0.0), G, 'T17' + ('.L' if s > 0 else '.R'), seats=('T03p.L' if s > 0 else 'T03p.R', 'T03b')) # T02 side details: recessed slot plates, slot panels, rear glands, screws, back strips and latch for s in (1, -1): sd = '.L' if s > 0 else '.R' sp = rbox((0.150, 0.048, 0.005), 0.0015, ops=[('+z', -0.066, 0.066, -0.0165, 0.0165, -0.0035)], rbev=0.001) L.put(sp, frame((-0.066, s * 0.3625, 0.090), (0, s, 0), spin=math.pi / 2 if s > 0 else -math.pi / 2), G, 'T02c' + sd, seats=('T02',)) sp2 = rbox((0.17, 0.062, 0.005), 0.002, ops=[('+z', -0.077, 0.077, -0.0242, 0.0242, -0.0035)], rbev=0.004, rim_segs=2) L.put(sp2, frame((-0.236, s * 0.3625, 0.178), (0, s, 0), spin=math.pi / 2 if s > 0 else -math.pi / 2), W, 'T02f' + sd, seats=('T02',)) L.put(rings_stack([(0.066, 0.016, 'paint_white'), (0.048, 0.030, 'rubber')], 24, base=0.003), frame((-0.330, s * 0.3628, 0.072), (0, s, 0)), None, 'T02d' + sd, seats=('T02',)) for z in (0.132, 0.187, 0.220): L.put(screw(0.0055), frame((0.3518, s * 0.290, z), 'X'), None, 'T02e' + sd, seats=('T02',)) # back screw strips L.put(rbox((0.006, 0.0396, 0.170), 0.0015), Matrix.Translation((-0.4205, s * 0.114, 0.150)), W, 'T02a' + sd, seats=('T02',)) for z in (0.090, 0.150, 0.210): L.put(screw(0.0055), frame((-0.4235, s * 0.114, z), '-X'), None, 'T02a' + sd, seats=('T02a' + sd,)) # back latch: slot plate with a bent tab over its top lp = rbox((0.110, 0.205, 0.006), 0.0015, ops=[('+z', -0.022, 0.022, -0.085, 0.085, -0.004)], rbev=0.001) L.put(lp, frame((-0.4215, 0, 0.150), '-X', spin=math.pi / 2), G, 'T02b', seats=('T02',)) tab = [(-0.4305, 0, 0.215), (-0.450, 0, 0.215), (-0.450, 0, 0.255), (-0.405, 0, 0.255)] for yy in (-0.016, 0.016): L.put(tube([(x, yy, z) for x, _, z in tab], 0.0045, 10, 0.008), None, S, 'T02b', seats=('T02b', 'T02', 'T03')) _belt_drive(L) _cables(L) _markings(L) return L def _belt_drive(L): """T10 clamp, T11 bracket plates, T12 finned pulley + motor cap, T13 toothed belt, T14 idler""" pc = Vector((0.671, 0, 0.242)) # T11: two side plates from the housing's front down round the pulley axle plate = [(0.50, 0.215, 0.01), (0.60, 0.205, 0.03), (0.725, 0.175, 0.05), (0.755, 0.242, 0.03), (0.735, 0.330, 0.03), (0.55, 0.360, 0.02), (0.50, 0.340, 0.01)] L.put(xz_slab(plate, 0.008, 0.028, 0.003, 0.003), mat=W, pid='T11', seats=('T03', 'T02')) L.put(xz_slab(plate, 0.126, 0.146, 0.003, 0.004), mat=W, pid='T11', seats=('T03', 'T02')) L.put(xz_slab(rrect(0.20, 0.016, 0.004, 0.635, 0.360), 0.008, 0.146, 0.003, 0.003), mat=W, pid='T11', seats=('T11', 'T03')) for x, z in ((0.540, 0.300), (0.600, 0.335), (0.730, 0.320)): L.put(screw(0.0055), frame((x, 0.146, z), 'Y'), None, 'T11s', seats=('T11',)) # T12: finned white pulley drum with a dark core and shaft, axis Y between the plates prof = [(0.0, 0.031, 0, G), (0.03, 0.031, 0, G), (0.03, 0.033, 0, W), (0.080, 0.033, 0.003, W)] y = 0.033 for k in range(6): prof += [(0.086, y + 0.004, 0.0, W), (0.086, y + 0.0105, 0.0, W), (0.079, y + 0.0125, 0, W), (0.079, y + 0.0145, 0, W)] y += 0.0145 prof += [(0.080, 0.121, 0.003, W), (0.03, 0.121, 0, G), (0.03, 0.124, 0, G), (0.0, 0.124, 0, G)] L.put(lathe(prof, 24, n=1), frame(pc, 'Y'), None, 'T12', seats=('T11',)) L.put(cyl(0.010, 0.140, 12, 0.001, S, z0=0.007), frame(pc, 'Y'), S, 'T12', seats=('T11', 'T12')) L.put(rings_stack([(0.128, 0.012, 'paint_white'), (0.097, 0.004, 'gunmetal'), (0.066, 0.006, 'glass_dark'), (0.030, 0.003, 'gunmetal')], 32, base=0.002), frame((pc.x, 0.146, pc.z), 'Y'), None, 'T12b', seats=('T11', 'T12')) L.put(rings_stack([(0.06, 0.006, 'gunmetal'), (0.03, 0.004, 'steel')], 24, base=0.002), frame((pc.x, 0.008, pc.z), '-Y'), None, 'T12', seats=('T11',)) # T14 idler under the front, on a small bracket from the lower block ic = Vector((0.561, 0.077, 0.110)) L.put(lathe([(0.0, 0.045, 0, S), (0.008, 0.045, 0, S), (0.008, 0.050, 0, G), (0.033, 0.050, 0.002, G), (0.033, 0.104, 0.002, G), (0.0, 0.104, 0, G)], 24), frame((ic.x, 0, ic.z), 'Y'), None, 'T14', seats=('T14b',)) L.put(rings_stack([(0.026, 0.006, 'steel')], 16, base=0.001), frame((ic.x, 0.104, ic.z), 'Y'), None, 'T14', seats=('T14',)) br = [(0.34, 0.075, 0.01), (0.585, 0.085, 0.02), (0.585, 0.135, 0.02), (0.34, 0.150, 0.01)] L.put(xz_slab(br, 0.022, 0.040, 0.002, 0.002), mat=G, pid='T14b', seats=('T02', 'T02core')) # T13 toothed belt: drive pulley -> hidden pulley in the lower block -> under the idler -> back _belt(L, pc, 0.0865, Vector((0.300, 0, 0.150)), 0.050, ic, 0.0335) # T10 clamp on top of the bracket, two halves and a bolt for z0, z1 in ((0.366, 0.404), (0.408, 0.452)): L.put(xz_slab(rrect(0.140, z1 - z0, 0.010, 0.690, (z0 + z1) / 2), 0.012, 0.150, 0.004, 0.005), mat=W, pid='T10', seats=('T11', 'T10')) L.put(hexbolt(0.020, 0.008), frame((0.690, 0.150, 0.43), 'Y'), None, 'T10b', seats=('T10',)) L.put(xz_slab(rrect(0.018, 0.006, 0.002, 0.645, 0.43), 0.1498, 0.1506, 0.0, 0.0), mat=O, pid='T10t', seats=('T10',), decal=True) def _belt(L, c1, r1, c2, r2, ci, ri, width=0.066, thick=0.012, pitch=0.0264, y0=0.044, y1=0.110, x_hide=0.355): """a closed toothed belt in the X-Z plane (Y y0..y1) round pulley c1 (front, radius r1) and the hidden pulley c2 (in the lower block), its lower run passing over the idler ci. Radii are the pulleys' surfaces.""" from dra_lib import face as _face h = thick / 2 def P(c, r, t): return Vector((c.x + r * math.cos(t), c.y + r * math.sin(t))) C1, C2, CI = Vector((c1.x, c1.z)), Vector((c2.x, c2.z)), Vector((ci.x, ci.z)) d = C2 - C1 ang = math.atan2(d.y, d.x) al = math.acos(max(-1.0, min(1.0, ((r1 + h) - (r2 + h)) / d.length))) up, lo = ang - al, ang + al # normal angles of the two external tangents if math.sin(up) < math.sin(lo): up, lo = lo, up pts = [] a0, a1 = up, lo # c1: clockwise from the upper tangent round the front while a1 > a0: a1 -= TAU n1 = 18 for k in range(n1 + 1): pts.append(P(C1, r1 + h, a0 + (a1 - a0) * k / n1)) for t in (math.radians(55), math.radians(90), math.radians(125)): # over the idler pts.append(P(CI, ri + h, t)) b0, b1 = lo, up # c2: clockwise from the lower tangent round the back while b0 < b1: b0 += TAU n2 = 10 for k in range(n2 + 1): pts.append(P(C2, r2 + h, b0 + (b1 - b0) * k / n2)) n = len(pts) bm = bmesh.new() rings = [] for i in range(n): t = (pts[(i + 1) % n] - pts[i - 1]).normalized() nrm = Vector((-t.y, t.x)) # outward for a clockwise loop ring = [bm.verts.new((pts[i].x + nrm.x * dn, yy, pts[i].y + nrm.y * dn)) for (yy, dn) in ((y0, -h), (y1, -h), (y1, h), (y0, h))] rings.append((ring, nrm)) for i in range(n): A, na = rings[i] B, nb = rings[(i + 1) % n] for k in range(4): k2 = (k + 1) % 4 mid = (A[k].co + A[k2].co + B[k].co + B[k2].co) / 4 cen = Vector(((pts[i].x + pts[(i + 1) % n].x) / 2, (y0 + y1) / 2, (pts[i].y + pts[(i + 1) % n].y) / 2)) _face(bm, [A[k], A[k2], B[k2], B[k]], R, out=mid - cen) L.put(bm, None, R, 'T13', seats=('T02', 'T02core', 'T03', 'T03b', 'T12', 'T14', 'T13t')) seg = [(pts[(i + 1) % n] - pts[i]).length for i in range(n)] total = sum(seg) s = pitch * 0.5 while s < total: acc = 0.0 for i in range(n): if acc + seg[i] >= s: f = (s - acc) / seg[i] p = pts[i].lerp(pts[(i + 1) % n], f) t = (pts[(i + 1) % n] - pts[i]).normalized() nrm = Vector((-t.y, t.x)) hidden = p.x < 0.552 and p.y > 0.238 # inside the front housing if p.x > x_hide and not hidden and (p - Vector((c2.x, c2.z))).length > r2 + 0.03 and (p - CI).length > ri + 0.03: M = Matrix(((t.x, 0, nrm.x), (0, 1, 0), (t.y, 0, nrm.y))).to_4x4() M.translation = Vector((p.x + nrm.x * (h + 0.0032), (y0 + y1) / 2, p.y + nrm.y * (h + 0.0032))) L.put(rbox((0.0105, y1 - y0 - 0.006, 0.0075), 0.0015, segs=1), M, R, 'T13t', seats=('T13', 'T02', 'T03', 'T03b', 'T11', 'T14', 'T14b', 'T02core', 'T12', 'T03p.L', 'T03p.R')) break acc += seg[i] s += pitch def _cables(L): """T09 seven-cable bundle from the top gland to the clamp; T15 side cable pairs (both sides) with ferrules""" # T08 bundle gland on the chamfer face, axis +X L.put(rings_stack([(0.136, 0.050, 'paint_white'), (0.124, 0.008, 'paint_white'), (0.106, 0.026, 'rubber')], 32, base=0.04), frame((0.488, 0.132, 0.700), 'X'), None, 'T08', seats=('T03', 'T03p.L', 'T03p.R')) hexo = [(0.0, 0.0)] + [(0.0245 * math.cos(TAU * k / 6), 0.0245 * math.sin(TAU * k / 6)) for k in range(6)] path = [(0.56, 0.132, 0.700), (0.66, 0.130, 0.706), (0.745, 0.115, 0.640), (0.765, 0.095, 0.540), (0.755, 0.085, 0.470), (0.720, 0.081, 0.440), (0.690, 0.081, 0.425)] from dra_lib import bundle L.put(bundle(path, 0.011, hexo, rad=0.07, segs=7), None, R, 'T09', seats=('T08', 'T10')) # T15 side pairs: from the glands T06 out and round, down to the rear corner glands T02d for s in (1, -1): sd = '.L' if s > 0 else '.R' path = [(0.187, s * 0.36, 0.440), (0.187, s * 0.47, 0.400), (0.13, s * 0.60, 0.270), (-0.01, s * 0.68, 0.170), (-0.18, s * 0.63, 0.105), (-0.29, s * 0.50, 0.072), (-0.330, s * 0.38, 0.072)] L.put(bundle(path, 0.0132, [(0.0, 0.028), (0.0, -0.028)], rad=0.09, segs=10), None, R, 'T15' + sd, seats=('T06' + sd, 'T02d' + sd)) def _markings(L): """orange paint shapes (thin floating geometry): hatch frames on the housing, warning square, line""" for s in (1, -1): M = L.on_surface((0.154, s * 0.6, 0.726), (0, -s, 0), up=(0, 0, 1)) if M is None: continue # a frame 0.154 x 0.044 with five 45-degree stripes, and a thin line above (local x = up the face, y across) bm = decal_strip([(-0.022, -0.077), (0.022, -0.077), (0.022, 0.077), (-0.022, 0.077)], 0.0026) for k in range(5): y0 = -0.060 + k * 0.026 st = decal_poly([(-0.018, y0 - 0.006), (-0.018, y0 + 0.004), (0.018, y0 + 0.016), (0.018, y0 + 0.006)]) merge(bm, st) st.free() ln = decal_poly([(0.030, -0.077), (0.034, -0.077), (0.034, 0.040), (0.030, 0.040)]) merge(bm, ln) ln.free() if s < 0: xform(bm, Matrix.Scale(-1, 4, Vector((0, 1, 0)))) L.decal(bm, M, 'T07' + ('.L' if s > 0 else '.R')) M = L.on_surface((-0.374, 0.6, 0.198), (0, -1, 0), up=(0, 0, 1)) if M is not None: bm = decal_strip([(-0.035, -0.030), (0.035, -0.030), (0.035, 0.030), (-0.035, 0.030)], 0.0024) for k in range(4): y0 = -0.024 + k * 0.016 st = decal_poly([(-0.030, y0), (-0.030, y0 + 0.006), (0.030, y0 + 0.012), (0.030, y0 + 0.006)]) merge(bm, st) st.free() L.decal(bm, M, 'T07a') M = L.on_surface((0.253, 0.6, 0.090), (0, -1, 0), up=(0, 0, 1)) if M is not None: L.decal(decal_poly([(-0.0017, -0.022), (0.0017, -0.022), (0.0017, 0.022), (-0.0017, 0.022)]), M, 'D10')