```python
import bpy, math, random
from math import radians as R, sin, cos, pi
from mathutils import Vector, Matrix, Euler

# ----------------------------------------------------------------------------- setup
bpy.ops.wm.read_factory_settings(use_empty=True)
scene = bpy.context.scene
scene.render.engine = 'CYCLES'
try:
    scene.cycles.device = 'GPU'
except Exception:
    pass
scene.cycles.samples = 64
try:
    scene.cycles.use_denoising = True
except Exception:
    pass
scene.render.resolution_x = 1536
scene.render.resolution_y = 1536
scene.render.film_transparent = False
try:
    scene.view_settings.view_transform = 'AgX'
except Exception:
    try:
        scene.view_settings.view_transform = 'Filmic'
    except Exception:
        pass
try:
    scene.view_settings.look = 'None'
except Exception:
    pass

def V(x, y, z):
    return Vector((x, y, z))

# ----------------------------------------------------------------------------- materials
def mat_pbr(name, color, metallic=0.0, rough=0.5, alpha=1.0, transmission=0.0,
            ior=1.45, emission=None, em_str=0.0, coat=0.0):
    m = bpy.data.materials.new(name)
    m.use_nodes = True
    nt = m.node_tree
    b = None
    for n in nt.nodes:
        if n.type == 'BSDF_PRINCIPLED':
            b = n
    if b is None:
        b = nt.nodes.new('ShaderNodeBsdfPrincipled')
    def st(k, v):
        if k in b.inputs:
            try:
                b.inputs[k].default_value = v
            except Exception:
                pass
    st('Base Color', (color[0], color[1], color[2], 1.0))
    st('Metallic', metallic)
    st('Roughness', rough)
    st('Alpha', alpha)
    st('Transmission Weight', transmission)
    st('IOR', ior)
    st('Coat Weight', coat)
    if emission is not None:
        st('Emission Color', (emission[0], emission[1], emission[2], 1.0))
        st('Emission Strength', em_str)
    if alpha < 1.0:
        try:
            m.blend_method = 'BLEND'
        except Exception:
            pass
        try:
            m.use_backface_culling = False
        except Exception:
            pass
    return m

MAT_ALU     = mat_pbr("Alu_Machined", (0.74, 0.755, 0.775), 1.0, 0.26)
MAT_ALU_OPT = mat_pbr("Alu_Generative", (0.66, 0.675, 0.695), 1.0, 0.48)
MAT_DARK    = mat_pbr("Anodized_Dark", (0.055, 0.058, 0.063), 0.9, 0.36)
MAT_STEEL   = mat_pbr("Steel_Polished", (0.62, 0.635, 0.66), 1.0, 0.13)
MAT_ACCENT  = mat_pbr("Anodized_Accent", (0.82, 0.30, 0.06), 0.75, 0.30)
MAT_POLY    = mat_pbr("Cable_Polymer", (0.02, 0.02, 0.022), 0.0, 0.42)
MAT_RUBBER  = mat_pbr("Contact_Pad", (0.035, 0.035, 0.038), 0.0, 0.82)
MAT_MEM     = mat_pbr("Technical_Membrane", (0.76, 0.83, 0.86), 0.0, 0.11,
                      alpha=0.26, transmission=0.65, ior=1.42)
MAT_DESK    = mat_pbr("Desk_Laminate", (0.72, 0.71, 0.69), 0.0, 0.42)
MAT_DESKED  = mat_pbr("Desk_Edge", (0.10, 0.10, 0.105), 0.2, 0.45)
MAT_FLOOR   = mat_pbr("Floor", (0.29, 0.295, 0.30), 0.0, 0.34)
MAT_WALL    = mat_pbr("Wall", (0.78, 0.785, 0.79), 0.0, 0.85)
MAT_SCREEN  = mat_pbr("Screen", (0.02, 0.03, 0.04), 0.0, 0.25,
                      emission=(0.13, 0.22, 0.30), em_str=1.4)

# ----------------------------------------------------------------------------- primitives
def place(o, M, loc=(0, 0, 0), rot=(0, 0, 0), scale=None):
    if scale is not None:
        o.data.transform(Matrix.Diagonal(Vector((scale[0], scale[1], scale[2], 1.0))))
    o.matrix_world = M @ Matrix.Translation(Vector(loc)) @ Euler(rot, 'XYZ').to_matrix().to_4x4()

def _fin(o, mat, smooth=False, name=None):
    if name:
        o.name = name
    if mat is not None:
        o.data.materials.append(mat)
    if smooth:
        try:
            bpy.ops.object.shade_auto_smooth(angle=R(35))
        except Exception:
            try:
                bpy.ops.object.shade_smooth()
            except Exception:
                pass
    return o

def add_cyl(M, r, h, loc=(0, 0, 0), rot=(0, 0, 0), mat=None, verts=48, name="cyl", smooth=True):
    bpy.ops.mesh.primitive_cylinder_add(vertices=verts, radius=r, depth=h, location=(0, 0, 0))
    o = bpy.context.object
    place(o, M, loc, rot)
    return _fin(o, mat, smooth and verts >= 20, name)

def add_box(M, sx, sy, sz, loc=(0, 0, 0), rot=(0, 0, 0), mat=None, bevel=0.0025, name="box"):
    bpy.ops.mesh.primitive_cube_add(size=1.0, location=(0, 0, 0))
    o = bpy.context.object
    place(o, M, loc, rot, scale=(sx, sy, sz))
    if bevel > 0:
        bm = o.modifiers.new('bev', 'BEVEL')
        bm.width = bevel
        bm.segments = 2
        bm.limit_method = 'ANGLE'
        bm.angle_limit = R(40)
    return _fin(o, mat, False, name)

def add_sphere(M, r, loc=(0, 0, 0), mat=None, sub=2, name="sph"):
    bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=sub, radius=r, location=(0, 0, 0))
    o = bpy.context.object
    place(o, M, loc)
    return _fin(o, mat, False, name)

def add_torus(M, R1, R2, loc=(0, 0, 0), rot=(0, 0, 0), mat=None, name="tor"):
    bpy.ops.mesh.primitive_torus_add(major_radius=R1, minor_radius=R2, major_segments=48,
                                     minor_segments=12, location=(0, 0, 0))
    o = bpy.context.object
    place(o, M, loc, rot)
    return _fin(o, mat, True, name)

def seg(M, p1, p2, r1, r2, mat=None, verts=12, name="seg"):
    p1 = Vector(p1); p2 = Vector(p2)
    d = p2 - p1
    L = d.length
    if L < 1e-6:
        return None
    bpy.ops.mesh.primitive_cone_add(vertices=verts, radius1=r1, radius2=r2, depth=L, location=(0, 0, 0))
    o = bpy.context.object
    e = d.to_track_quat('Z', 'Y').to_euler()
    place(o, M, tuple((p1 + p2) / 2.0), (e.x, e.y, e.z))
    return _fin(o, mat, False, name)

def bolt_ring(M, count, rr, pos, axis='Z', r=0.0045, h=0.006, mat=MAT_STEEL, phase=0.0):
    out = []
    for i in range(count):
        a = phase + 2 * pi * i / count
        if axis == 'Z':
            loc = (pos[0] + rr * cos(a), pos[1] + rr * sin(a), pos[2]); rot = (0, 0, a)
        elif axis == 'Y':
            loc = (pos[0] + rr * cos(a), pos[1], pos[2] + rr * sin(a)); rot = (pi / 2, 0, 0)
        else:
            loc = (pos[0], pos[1] + rr * cos(a), pos[2] + rr * sin(a)); rot = (0, pi / 2, 0)
        out.append(add_cyl(M, r, h, loc, rot, mat, verts=6, name="bolt", smooth=False))
    return out

def join_objs(objs, name):
    objs = [o for o in objs if o is not None]
    bpy.ops.object.select_all(action='DESELECT')
    for o in objs:
        o.select_set(True)
    bpy.context.view_layer.objects.active = objs[0]
    bpy.ops.object.join()
    o = bpy.context.object
    o.name = name
    return o

# ----------------------------------------------------------------------------- kinematic frames
DESK_H = 0.75
BASE_XY = (-0.25, 0.22)
TARGET_XY = (0.20, -0.15)
YAW = math.atan2(TARGET_XY[1] - BASE_XY[1], TARGET_XY[0] - BASE_XY[0])

M_static = Matrix.Translation((BASE_XY[0], BASE_XY[1], DESK_H))
M_base = M_static @ Matrix.Rotation(YAW, 4, 'Z')
H_SH = 0.22
M_sh = M_base @ Matrix.Translation((0, 0, H_SH))
M_A = M_sh @ Matrix.Rotation(R(45.05), 4, 'Y')
LA = 0.42
M_el = M_A @ Matrix.Translation((0, 0, LA))
M_B = M_el @ Matrix.Rotation(R(91.15), 4, 'Y')
LB = 0.36
M_wr = M_B @ Matrix.Translation((0, 0, LB))
M_G = M_wr @ Matrix.Rotation(R(33.8), 4, 'Y')
M_G2 = M_G @ Matrix.Rotation(R(90.0), 4, 'Z')
GRIP = M_G2 @ Vector((0, 0, 0.21))

# ----------------------------------------------------------------------------- room + desk
bpy.ops.mesh.primitive_plane_add(size=16, location=(0, 0, 0))
_fin(bpy.context.object, MAT_FLOOR, False, "Floor")

bpy.ops.mesh.primitive_plane_add(size=16, location=(0, 0, 0))
w = bpy.context.object
place(w, Matrix.Translation((0, 1.95, 2.4)), rot=(pi / 2, 0, 0))
_fin(w, MAT_WALL, False, "Backdrop")

bpy.ops.mesh.primitive_plane_add(size=16, location=(0, 0, 0))
w2 = bpy.context.object
place(w2, Matrix.Translation((-3.2, 0, 2.4)), rot=(0, pi / 2, 0))
_fin(w2, MAT_WALL, False, "SideWall")

MD = Matrix.Identity(4)
add_box(MD, 1.74, 0.94, 0.040, (0, 0, DESK_H - 0.020), mat=MAT_DESK, bevel=0.004, name="DeskTop")
add_box(MD, 1.72, 0.92, 0.014, (0, 0, DESK_H - 0.046), mat=MAT_DESKED, bevel=0.002, name="DeskCore")
for sx in (-1, 1):
    for sy in (-1, 1):
        add_box(MD, 0.05, 0.05, DESK_H - 0.058, (sx * 0.78, sy * 0.40, (DESK_H - 0.058) / 2),
                mat=MAT_DARK, bevel=0.003, name="DeskLeg")
for sx in (-1, 1):
    add_box(MD, 0.035, 0.74, 0.030, (sx * 0.78, 0, 0.12), mat=MAT_DARK, bevel=0.002, name="DeskBrace")
add_box(MD, 1.50, 0.035, 0.030, (0, 0, 0.12), mat=MAT_DARK, bevel=0.002, name="DeskBrace2")

# desk props (restrained lab context)
add_box(MD, 0.24, 0.17, 0.010, (-0.62, -0.10, DESK_H + 0.005), mat=MAT_DARK, bevel=0.002, name="Tray")
for i, (dx, dy, rr) in enumerate([(-0.05, 0.02, 0.022), (0.045, -0.03, 0.017)]):
    add_cyl(MD, rr, 0.010, (-0.62 + dx, -0.10 + dy, DESK_H + 0.015), mat=MAT_STEEL, name="TrayPart")
add_cyl(MD, 0.010, 0.012, (-0.62 + 0.045, -0.10 - 0.03, DESK_H + 0.026), mat=MAT_ALU, verts=6, name="TrayHex")

MDISP = Matrix.Translation((0.56, 0.34, 0)) @ Matrix.Rotation(R(-12), 4, 'Z')
add_box(MDISP, 0.34, 0.020, 0.20, (0, 0, DESK_H + 0.145), mat=MAT_DARK, bevel=0.003, name="DisplayBody")
add_box(MDISP, 0.30, 0.004, 0.165, (0, -0.013, DESK_H + 0.145), mat=MAT_SCREEN, bevel=0.0, name="Screen")
add_box(MDISP, 0.10, 0.11, 0.012, (0, 0.01, DESK_H + 0.010), mat=MAT_ALU, bevel=0.002, name="DisplayFoot")
add_box(MDISP, 0.035, 0.035, 0.052, (0, 0.005, DESK_H + 0.040), mat=MAT_ALU, bevel=0.002, name="DisplayStem")

# ----------------------------------------------------------------------------- target object
OX, OY = GRIP.x, GRIP.y
MO = Matrix.Translation((OX, OY, 0))
add_cyl(MO, 0.026, 0.010, (0, 0, DESK_H + 0.005), mat=MAT_ALU, name="PartFlange")
bolt_ring(MO, 4, 0.020, (0, 0, DESK_H + 0.011), 'Z', 0.003, 0.004, MAT_STEEL)
add_cyl(MO, 0.021, 0.056, (0, 0, DESK_H + 0.038), mat=MAT_STEEL, name="PartBody")
add_cyl(MO, 0.0215, 0.004, (0, 0, DESK_H + 0.030), mat=MAT_ACCENT, name="PartRing")
add_cyl(MO, 0.011, 0.012, (0, 0, DESK_H + 0.072), mat=MAT_ALU, name="PartBoss")
add_cyl(MO, 0.005, 0.008, (0, 0, DESK_H + 0.080), mat=MAT_DARK, name="PartHole")

# ----------------------------------------------------------------------------- base assembly (static)
add_box(M_static, 0.225, 0.225, 0.014, (0, 0, 0.007), mat=MAT_DARK, bevel=0.004, name="MountPlate")
for sx in (-1, 1):
    for sy in (-1, 1):
        add_cyl(M_static, 0.0085, 0.009, (sx * 0.090, sy * 0.090, 0.0175), mat=MAT_STEEL, verts=6,
                name="MountBolt", smooth=False)
        add_cyl(M_static, 0.011, 0.004, (sx * 0.090, sy * 0.090, 0.0155), mat=MAT_ALU, name="Washer")
add_cyl(M_static, 0.081, 0.007, (0, 0, 0.0175), mat=MAT_DARK, name="PedestalRing")
add_cyl(M_static, 0.078, 0.038, (0, 0, 0.040), mat=MAT_ALU, name="Pedestal")
for i in range(6):
    a = 2 * pi * i / 6 + 0.3
    add_box(M_static, 0.010, 0.022, 0.030, (0.077 * cos(a), 0.077 * sin(a), 0.040), (0, 0, a),
            mat=MAT_ALU, bevel=0.0015, name="PedRib")
add_cyl(M_static, 0.073, 0.012, (0, 0, 0.065), mat=MAT_STEEL, name="BearingRace")

# rotating platform (yaw)
add_cyl(M_base, 0.0705, 0.003, (0, 0, 0.0725), mat=MAT_DARK, name="YawSeam")
add_cyl(M_base, 0.070, 0.020, (0, 0, 0.084), mat=MAT_ALU, name="Turntable")
add_cyl(M_base, 0.058, 0.005, (0, 0, 0.0965), mat=MAT_ACCENT, name="YawAccent")
bolt_ring(M_base, 8, 0.052, (0, 0, 0.099), 'Z', 0.0045, 0.007, MAT_STEEL)
add_cyl(M_base, 0.050, 0.050, (0, 0, 0.124), mat=MAT_DARK, name="YawHousing")
add_cyl(M_base, 0.052, 0.008, (0, 0, 0.152), mat=MAT_ALU, name="YawCap")
# yaw drive gearbox tucked at rear of platform
add_box(M_base, 0.052, 0.062, 0.046, (-0.072, 0, 0.106), mat=MAT_DARK, bevel=0.003, name="YawGearbox")
add_cyl(M_base, 0.019, 0.040, (-0.072, 0, 0.075), mat=MAT_ALU, name="YawMotor")
bolt_ring(M_base, 4, 0.028, (-0.072, 0, 0.130), 'Z', 0.0035, 0.005, MAT_STEEL)

# shoulder yoke
for sy in (-1, 1):
    add_box(M_base, 0.086, 0.014, 0.135, (0, sy * 0.055, 0.1675), mat=MAT_ALU, bevel=0.003, name="YokePlate")
    add_cyl(M_base, 0.033, 0.020, (0, sy * 0.055, H_SH), (pi / 2, 0, 0), mat=MAT_ALU, name="YokeBoss")
    add_cyl(M_base, 0.028, 0.007, (0, sy * 0.068, H_SH), (pi / 2, 0, 0), mat=MAT_DARK, name="YokeCover")
    bolt_ring(M_base, 6, 0.021, (0, sy * 0.0725, H_SH), 'Y', 0.0035, 0.005, MAT_STEEL)
    add_box(M_base, 0.030, 0.013, 0.030, (-0.040, sy * 0.055, 0.108), (0, R(35), 0), mat=MAT_ALU,
            bevel=0.002, name="YokeGusset")
add_cyl(M_base, 0.042, 0.050, (0, 0.100, H_SH), (pi / 2, 0, 0), mat=MAT_DARK, name="ShoulderGearbox")
add_cyl(M_base, 0.036, 0.010, (0, 0.130, H_SH), (pi / 2, 0, 0), mat=MAT_ALU, name="ShoulderCover")
add_cyl(M_base, 0.020, 0.004, (0, 0.137, H_SH), (pi / 2, 0, 0), mat=MAT_ACCENT, name="ShoulderAccent")
bolt_ring(M_base, 6, 0.029, (0, 0.136, H_SH), 'Y', 0.0035, 0.005, MAT_STEEL)
add_cyl(M_base, 0.030, 0.022, (0, -0.098, H_SH), (pi / 2, 0, 0), mat=MAT_ALU, name="Encoder")
add_cyl(M_base, 0.022, 0.006, (0, -0.111, H_SH), (pi / 2, 0, 0), mat=MAT_DARK, name="EncoderCap")
add_cyl(M_base, 0.013, 0.190, (0, 0, H_SH), (pi / 2, 0, 0), mat=MAT_STEEL, name="ShoulderPin")

# ----------------------------------------------------------------------------- generative struts
def make_strut(M, L, wmid, wend, ymid, yend, rend, rmid, dr, name, voxel=0.005):
    objs = []
    ts = [0.0, 0.14, 0.32, 0.5, 0.68, 0.86, 1.0]
    def ax(t): return wend + (wmid - wend) * (sin(pi * t) ** 0.6)
    def ay(t): return yend + (ymid - yend) * (sin(pi * t) ** 0.6)
    def rr(t): return rmid + (rend - rmid) * (1.0 - sin(pi * t) ** 0.8)
    def P(sx, sy, t): return Vector((sx * ax(t), sy * ay(t), t * L))
    signs = [(1, 1), (1, -1), (-1, 1), (-1, -1)]
    for sx, sy in signs:
        for i in range(len(ts) - 1):
            objs.append(seg(M, P(sx, sy, ts[i]), P(sx, sy, ts[i + 1]),
                            rr(ts[i]), rr(ts[i + 1]), MAT_ALU_OPT, 12))
        for t in ts:
            objs.append(add_sphere(M, rr(t) * 1.03, tuple(P(sx, sy, t)), MAT_ALU_OPT, 2))
    for sx in (1, -1):
        for i in range(len(ts) - 1):
            s1 = 1 if (i % 2 == 0) else -1
            a = P(sx, s1, ts[i]); b = P(sx, -s1, ts[i + 1])
            objs.append(seg(M, a, b, dr, dr * 0.85, MAT_ALU_OPT, 10))
    for sy in (1, -1):
        for i in range(len(ts) - 1):
            s1 = 1 if (i % 2 == 1) else -1
            a = P(s1, sy, ts[i]); b = P(-s1, sy, ts[i + 1])
            objs.append(seg(M, a, b, dr * 0.85, dr * 0.75, MAT_ALU_OPT, 10))
    hub0 = max(ax(0.0), ay(0.0)) + rend * 0.95
    hub1 = max(ax(1.0), ay(1.0)) + rend * 0.95
    objs.append(add_cyl(M, hub0, 0.030, (0, 0, 0.009), mat=MAT_ALU_OPT, verts=32, name="hub", smooth=False))
    objs.append(add_cyl(M, hub1, 0.030, (0, 0, L - 0.009), mat=MAT_ALU_OPT, verts=32, name="hub", smooth=False))
    o = join_objs(objs, name)
    rm = o.modifiers.new('remesh', 'REMESH')
    rm.mode = 'VOXEL'
    rm.voxel_size = voxel
    try:
        rm.use_smooth_shade = True
    except Exception:
        pass
    sm = o.modifiers.new('smooth', 'SMOOTH')
    sm.factor = 0.55
    sm.iterations = 6
    return o

# ---- link A hardware + strut
add_cyl(M_A, 0.047, 0.012, (0, 0, 0.030), mat=MAT_DARK, name="LinkA_Flange")
bolt_ring(M_A, 6, 0.035, (0, 0, 0.037), 'Z', 0.0035, 0.005, MAT_STEEL)
M_sA = M_A @ Matrix.Translation((0, 0, 0.045))
make_strut(M_sA, 0.330, 0.058, 0.020, 0.032, 0.018, 0.016, 0.0100, 0.0072, "Strut_LinkA_Optimized")
add_cyl(M_A, 0.047, 0.012, (0, 0, 0.390), mat=MAT_DARK, name="LinkA_Flange2")
bolt_ring(M_A, 6, 0.035, (0, 0, 0.383), 'Z', 0.0035, 0.005, MAT_STEEL)
# membranes on link A
add_box(M_sA, 0.066, 0.0016, 0.095, (0, 0.028, 0.100), mat=MAT_MEM, bevel=0.0, name="Membrane_A1")
add_box(M_sA, 0.0016, 0.045, 0.100, (-0.052, 0, 0.210), mat=MAT_MEM, bevel=0.0, name="Membrane_A2")

# ---- elbow assembly
add_cyl(M_el, 0.042, 0.088, (0, 0, 0), (pi / 2, 0, 0), mat=MAT_DARK, name="ElbowHousing")
for sy in (-1, 1):
    add_cyl(M_el, 0.044, 0.010, (0, sy * 0.048, 0), (pi / 2, 0, 0), mat=MAT_ALU, name="ElbowCover")
    bolt_ring(M_el, 6, 0.032, (0, sy * 0.054, 0), 'Y', 0.0035, 0.005, MAT_STEEL)
    add_cyl(M_el, 0.019, 0.004, (0, sy * 0.055, 0), (pi / 2, 0, 0), mat=MAT_ACCENT, name="ElbowAccent")
add_cyl(M_el, 0.012, 0.118, (0, 0, 0), (pi / 2, 0, 0), mat=MAT_STEEL, name="ElbowPin")
add_box(M_el, 0.060, 0.055, 0.060, (-0.070, 0, 0.015), mat=MAT_DARK, bevel=0.003, name="ElbowGearbox")
add_cyl(M_el, 0.026, 0.055, (-0.070, 0.060, 0.015), (pi / 2, 0, 0), mat=MAT_ALU, name="ElbowMotor")
add_cyl(M_el, 0.020, 0.007, (-0.070, 0.091, 0.015), (pi / 2, 0, 0), mat=MAT_DARK, name="ElbowMotorCap")
bolt_ring(M_el, 4, 0.032, (-0.070, 0, 0.048), 'Z', 0.0035, 0.005, MAT_STEEL)

# ---- link B hardware + strut
add_cyl(M_B, 0.041, 0.011, (0, 0, 0.030), mat=MAT_DARK, name="LinkB_Flange")
bolt_ring(M_B, 6, 0.030, (0, 0, 0.036), 'Z', 0.003, 0.005, MAT_STEEL)
M_sB = M_B @ Matrix.Translation((0, 0, 0.045))
make_strut(M_sB, 0.270, 0.042, 0.016, 0.025, 0.014, 0.0135, 0.0088, 0.0062, "Strut_LinkB_Optimized", 0.0045)
add_cyl(M_B, 0.041, 0.011, (0, 0, 0.331), mat=MAT_DARK, name="LinkB_Flange2")
bolt_ring(M_B, 6, 0.030, (0, 0, 0.325), 'Z', 0.003, 0.005, MAT_STEEL)
add_box(M_sB, 0.048, 0.0016, 0.078, (0, -0.022, 0.115), mat=MAT_MEM, bevel=0.0, name="Membrane_B1")

# ---- wrist assembly
add_cyl(M_wr, 0.032, 0.066, (0, 0, 0), (pi / 2, 0, 0), mat=MAT_DARK, name="WristHousing")
for sy in (-1, 1):
    add_cyl(M_wr, 0.034, 0.008, (0, sy * 0.037, 0), (pi / 2, 0, 0), mat=MAT_ALU, name="WristCover")
    bolt_ring(M_wr, 6, 0.024, (0, sy * 0.042, 0), 'Y', 0.003, 0.004, MAT_STEEL)
add_cyl(M_wr, 0.010, 0.090, (0, 0, 0), (pi / 2, 0, 0), mat=MAT_STEEL, name="WristPin")
add_box(M_wr, 0.048, 0.050, 0.046, (-0.050, 0, 0.002), mat=MAT_DARK, bevel=0.003, name="WristActuator")
add_cyl(M_wr, 0.019, 0.046, (-0.050, 0.046, 0.002), (pi / 2, 0, 0), mat=MAT_ALU, name="WristMotor")
add_cyl(M_wr, 0.014, 0.006, (-0.050, 0.072, 0.002), (pi / 2, 0, 0), mat=MAT_DARK, name="WristMotorCap")

# roll unit
add_cyl(M_G, 0.030, 0.046, (0, 0, 0.042), mat=MAT_ALU, name="RollHousing")
add_cyl(M_G, 0.0305, 0.003, (0, 0, 0.052), mat=MAT_DARK, name="RollSeam")
add_cyl(M_G, 0.026, 0.004, (0, 0, 0.060), mat=MAT_ACCENT, name="RollAccent")

# ----------------------------------------------------------------------------- gripper (two-finger parallel)
MG = M_G2
add_cyl(MG, 0.032, 0.008, (0, 0, 0.069), mat=MAT_DARK, name="ToolPlate")
bolt_ring(MG, 4, 0.023, (0, 0, 0.075), 'Z', 0.003, 0.004, MAT_STEEL)
add_box(MG, 0.058, 0.052, 0.055, (0, 0, 0.101), mat=MAT_ALU, bevel=0.003, name="GripperBody")
add_cyl(MG, 0.014, 0.050, (-0.036, 0, 0.090), (pi / 2, 0, 0), mat=MAT_DARK, name="GripDriveMotor")
add_cyl(MG, 0.010, 0.006, (-0.036, 0.028, 0.090), (pi / 2, 0, 0), mat=MAT_ALU, name="GripMotorCap")
add_box(MG, 0.062, 0.050, 0.008, (0, 0, 0.132), mat=MAT_DARK, bevel=0.002, name="CarriagePlate")
add_box(MG, 0.100, 0.012, 0.010, (0, 0, 0.141), mat=MAT_STEEL, bevel=0.0015, name="LinearRail")
add_box(MG, 0.100, 0.006, 0.004, (0, 0.010, 0.141), mat=MAT_DARK, bevel=0.0, name="RailStrip")

for sx in (-1, 1):
    add_box(MG, 0.026, 0.034, 0.026, (sx * 0.038, 0, 0.150), mat=MAT_ALU, bevel=0.002, name="FingerCarriage")
    add_cyl(MG, 0.005, 0.044, (sx * 0.038, 0, 0.150), (pi / 2, 0, 0), mat=MAT_STEEL, name="FingerPivot")
    add_cyl(MG, 0.008, 0.004, (sx * 0.038, 0.021, 0.150), (pi / 2, 0, 0), mat=MAT_DARK, name="PivotCap")
    add_box(MG, 0.014, 0.028, 0.066, (sx * 0.038, 0, 0.188), mat=MAT_ALU, bevel=0.002, name="FingerBlade")
    add_box(MG, 0.015, 0.026, 0.032, (sx * 0.038, 0, 0.232), mat=MAT_ALU, bevel=0.002, name="FingerTip")
    add_box(MG, 0.006, 0.024, 0.038, (sx * 0.0295, 0, 0.212), mat=MAT_RUBBER, bevel=0.001, name="ContactPad")
    for dy in (-0.009, 0.009):
        add_cyl(MG, 0.0022, 0.006, (sx * 0.0325, dy, 0.212), (0, pi / 2, 0), mat=MAT_STEEL,
                verts=6, name="PadScrew", smooth=False)
    for dy in (-0.020, 0.020):
        seg(MG, (sx * 0.010, dy, 0.140), (sx * 0.030, dy, 0.152), 0.0028, 0.0028, MAT_STEEL, 10, "DriveLink")
    add_box(MG, 0.010, 0.010, 0.014, (sx * 0.038, 0.020, 0.166), mat=MAT_DARK, bevel=0.001, name="FingerSensor")

# ----------------------------------------------------------------------------- cabling (slim, controlled)
def cable(points, r=0.0034, mat=MAT_POLY, name="Cable"):
    cu = bpy.data.curves.new(name, 'CURVE')
    cu.dimensions = '3D'
    sp = cu.splines.new('BEZIER')
    sp.bezier_points.add(len(points) - 1)
    for i, p in enumerate(points):
        bp = sp.bezier_points[i]
        bp.co = (p[0], p[1], p[2])
        bp.handle_left_type = 'AUTO'
        bp.handle_right_type = 'AUTO'
    cu.bevel_depth = r
    cu.bevel_resolution = 3
    cu.resolution_u = 6
    cu.use_fill_caps = True
    o = bpy.data.objects.new(name, cu)
    bpy.context.collection.objects.link(o)
    o.data.materials.append(mat)
    bpy.ops.object.select_all(action='DESELECT')
    o.select_set(True)
    bpy.context.view_layer.objects.active = o
    bpy.ops.object.convert(target='MESH')
    return bpy.context.object

c1 = [M_base @ V(-0.062, -0.040, 0.100),
      M_base @ V(-0.078, -0.062, 0.150),
      M_base @ V(-0.070, -0.075, 0.200),
      M_sh @ V(-0.062, -0.062, 0.010),
      M_A @ V(-0.062, -0.030, 0.070),
      M_A @ V(-0.086, -0.028, 0.180),
      M_A @ V(-0.064, -0.028, 0.310),
      M_el @ V(-0.055, -0.030, -0.010),
      M_el @ V(-0.092, -0.035, 0.020)]
cable(c1, 0.0036, MAT_POLY, "CableRun_Lower")

c2 = [M_el @ V(-0.090, 0.030, 0.030),
      M_B @ V(-0.056, 0.024, 0.060),
      M_B @ V(-0.070, 0.022, 0.170),
      M_B @ V(-0.052, 0.022, 0.290),
      M_wr @ V(-0.046, 0.030, -0.012),
      M_wr @ V(-0.058, 0.030, 0.018),
      M_G @ V(-0.034, 0.024, 0.045)]
cable(c2, 0.0030, MAT_POLY, "CableRun_Upper")

for M_, loc in [(M_A, (-0.074, -0.028, 0.120)), (M_A, (-0.074, -0.028, 0.250)),
                (M_B, (-0.062, 0.023, 0.120)), (M_B, (-0.060, 0.023, 0.240)),
                (M_base, (-0.070, -0.068, 0.170))]:
    add_box(M_, 0.020, 0.014, 0.011, loc, mat=MAT_DARK, bevel=0.0015, name="CableClamp")

# ----------------------------------------------------------------------------- lighting
def add_area(name, loc, target, energy, sx, sy=None, shape='RECTANGLE'):
    ld = bpy.data.lights.new(name, 'AREA')
    ld.energy = energy
    ld.shape = shape
    ld.size = sx
    if sy is not None and shape == 'RECTANGLE':
        ld.size_y = sy
    o = bpy.data.objects.new(name, ld)
    bpy.context.collection.objects.link(o)
    o.location = Vector(loc)
    d = Vector(target) - Vector(loc)
    o.rotation_euler = d.to_track_quat('-Z', 'Y').to_euler()
    return o

add_area("KeyLight", (1.45, -1.55, 2.15), (0.0, 0.02, 1.00), 520, 1.4, 1.0)
add_area("FillLight", (-1.85, -1.35, 1.55), (-0.10, 0.10, 0.92), 230, 2.4, 2.0)
add_area("RimLight", (-0.35, 1.25, 2.05), (0.02, 0.15, 1.02), 260, 1.0, 0.4)
add_area("TopSoft", (0.20, -0.15, 2.95), (0.10, 0.00, 0.92), 340, 2.6, 2.6)

world = bpy.data.worlds.new("World")
scene.world = world
world.use_nodes = True
bg = world.node_tree.nodes.get('Background')
if bg:
    bg.inputs[0].default_value = (0.34, 0.35, 0.375, 1.0)
    bg.inputs[1].default_value = 0.40

# ----------------------------------------------------------------------------- camera
cam_data = bpy.data.cameras.new("Camera")
cam_data.lens = 50.0
cam_data.sensor_width = 36.0
cam = bpy.data.objects.new("Camera", cam_data)
bpy.context.collection.objects.link(cam)
cam.location = Vector((1.05, -1.45, 1.30))
look_at = Vector((0.02, 0.03, 1.00))
cam.rotation_euler = (look_at - cam.location).to_track_quat('-Z', 'Y').to_euler()
try:
    cam_data.dof.use_dof = True
    cam_data.dof.focus_distance = (look_at - cam.location).length
    cam_data.dof.aperture_fstop = 5.0
except Exception:
    pass
scene.camera = cam

# ----------------------------------------------------------------------------- save
try:
    if bpy.data.filepath:
        bpy.ops.wm.save_mainfile()
    else:
        bpy.ops.wm.save_as_mainfile(filepath="/tmp/robotic_arm_scene.blend")
except Exception:
    pass
```