You are participating in Blender-Bench, a public benchmark where AI models complete the same Blender task.

Your result will be compared head-to-head with another model. Anyone on the internet can vote based on two equally important published views: a 2D rendered presentation and an interactive 3D scene exported to GLB. Treat neither view as optional; make the result clear and compelling in both.

Complete the request as well as you can. The benchmark does not prescribe a workflow or visual style; use whichever Blender techniques you consider appropriate. Make the important requirements evident in the published result, save a complete scene, and present the work clearly in every requested output.

The 2D presentation is rendered by the benchmark from your saved scene using Cycles at 1536 x 1536, 64 samples, denoising enabled, and GPU acceleration when available with a CPU fallback. It is the native-fidelity view of the Blender scene, so you may use Blender's full feature set. Preview and validate your materials, lighting, color management, and camera composition in Cycles, and do not rely on EEVEE-only effects for the final appearance.

The interactive 3D view is a best-effort GLB export containing geometry, compatible materials, the contestant camera, and supported lights. Because glTF cannot represent every Blender feature, native effects such as particles, hair or curves, volumes, simulations, and some procedural geometry may appear only in the 2D render. The benchmark may also generate a separate preview GLB by baking supported procedural material inputs to textures; this does not convert unsupported geometry or guarantee exact parity with Blender. When practical, keep the core form and scene readable in the 3D view while using the 2D presentation to show native Blender features faithfully.

## Harness Instructions

You are writing a single Python script for Blender 5.1 using the bpy API. The scene starts empty. Your script must build everything the user's prompt asks for.

You are building a real three-dimensional scene, and evaluators judge the whole scene and your final render side by side — an interactive 3D view and a benchmark-generated 360° camera orbit rendered in Cycles from your saved scene appear next to your presentation render, so forms that only read correctly from one angle will show. Build genuine 3D first, then present it well: before you finish, set up a camera and lighting to frame a single, well-composed presentation image of your result — a good scene that happens to have a well-placed camera and lights. The final benchmark capture overrides the scene render settings and renders in Cycles at 1536x1536, 64 samples, with denoising and GPU acceleration (CPU fallback). Preview and validate materials, lighting, color management, and composition in Cycles; do not rely on EEVEE-only effects. Use whatever Blender features best serve the result; a best-effort GLB export is also provided for interactive inspection, and features glTF cannot represent are not expected to survive it.

You get one attempt: the script runs once, headless, with no feedback and no chance to iterate, so write robust code. Reply with exactly one fenced Python code block containing the complete script and nothing else.


## Task

Complete the following Blender task:

<task>

A desk-mounted robotic arm with at least four articulated joints, posed mid-reach toward a small object on the desk — not a generic industrial catalog robot, but a refined experimental machine built from a combination of precision mechanical assemblies and selectively generative structural elements.

The arm’s main load-bearing struts are topology-optimized: organic metal members with smooth branching transitions, controlled lattice openings, and carefully shaped voids where material has been removed according to structural load. These parametric forms should appear only on the elongated structural links between joints. They must not continue into the base, rotary joints, wrist, actuators, or end effector. Each optimized strut should feel like a purpose-grown structural component installed within an otherwise conventional mechanical system.

All joints and connection points should be clearly assembled engineering components: machined cylindrical housings, bearings, rotary actuators, bolted flanges, hinge pins, compact gearboxes, brackets, covers, and replaceable interface plates. The base should be a clean, stable mechanical assembly with a visible rotating platform and mounting hardware. Avoid ornamental latticework in these areas. The contrast between the organic optimized struts and the precise modular joint assemblies is the design centerpiece.

Remove exposed corrugated pipes, aged tubing, rust, corrosion, or distressed materials. Cable routing should be minimal and controlled, using slim black electrical lines, internal channels, recessed cable guides, or compact articulated conduits integrated close to the structure. Nothing should appear improvised, draped, weathered, or steampunk.

Instead of heavy woven fabric, use a very restrained translucent technical membrane only where useful. Thin semi-transparent polymer panels or tensioned mesh can span selected openings within the optimized struts, lightly revealing the structure beneath. The material should resemble clear reinforced film, translucent TPU, or fine architectural mesh rather than carbon fiber or ballistic cloth. It should feel protective and lightweight, not decorative. This membrane may also be omitted from some segments so the metal structure remains visually dominant.

The manipulator should not use a three-clawed radial gripper. Replace it with a compact two-finger parallel gripper or a refined adaptive pincer mechanism. The fingers should be mechanically designed from solid machined components with small replaceable contact pads, visible pivots, and believable actuation. Do not use topology-optimized lattice geometry on the fingers. The end effector should look precise, stable, and suitable for picking up small desk objects rather than aggressive or creature-like.

The mechanics must remain fully believable: visible rotational axes at every joint, plausible actuator placement, clear mechanical interfaces between segments, realistic joint limits, and sensible tapering from the heavy base to the smaller wrist and gripper. The pose must respect the joint layout, with no intersecting parts, impossible bends, unsupported structures, or decorative mechanisms that do not explain their function.

Present the arm in a clean contemporary design studio or robotics lab, reaching toward a small machined object on a desk. Use premium product-design lighting, a neutral background, crisp material separation, and enough detail to show how the arm is assembled. The final design should feel like a functional research prototype combining generative engineering with disciplined industrial design.

</task>


```python
import bpy
import math
from mathutils import Vector, Matrix

# ---------------------------------------------------------------------------
# Scene helpers
# ---------------------------------------------------------------------------

def clear_scene():
    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete(use_global=False)

def hex_rgb(h):
    h = h.lstrip('#')
    return tuple(int(h[i:i + 2], 16) / 255.0 for i in (0, 2, 4))

def new_material(name, color, rough=0.4, metal=0.0, clearcoat=0.0):
    mat = bpy.data.materials.new(name)
    mat.use_nodes = True
    bsdf = mat.node_tree.nodes.get('Principled BSDF')
    bsdf.inputs['Base Color'].default_value = (*color, 1.0)
    bsdf.inputs['Roughness'].default_value = rough
    bsdf.inputs['Metallic'].default_value = metal
    if 'Clearcoat' in bsdf.inputs:
        bsdf.inputs['Clearcoat'].default_value = clearcoat
    return mat

def new_membrane_material(name, color, rough=0.15, trans=0.5, alpha=0.22):
    mat = bpy.data.materials.new(name)
    mat.use_nodes = True
    bsdf = mat.node_tree.nodes.get('Principled BSDF')
    bsdf.inputs['Base Color'].default_value = (*color, 1.0)
    bsdf.inputs['Roughness'].default_value = rough
    bsdf.inputs['Metallic'].default_value = 0.0
    bsdf.inputs['Transmission'].default_value = trans
    bsdf.inputs['Alpha'].default_value = alpha
    mat.blend_method = 'BLEND'
    mat.shadow_mode = 'NONE'
    return mat

def align_axis(obj, direction):
    direction = Vector(direction).normalized()
    obj.rotation_mode = 'QUATERNION'
    obj.rotation_quaternion = direction.to_track_quat('Z', 'Y')

def aim_obj(obj, target):
    direction = Vector(target) - Vector(obj.location)
    obj.rotation_mode = 'QUATERNION'
    obj.rotation_quaternion = direction.to_track_quat('-Z', 'Y')

def add_cylinder(name, radius, depth, loc=(0, 0, 0), rot=(0, 0, 0),
                 mat=None, verts=48, scale=(1, 1, 1)):
    bpy.ops.mesh.primitive_cylinder_add(vertices=verts, radius=radius,
                                        depth=depth, location=loc)
    obj = bpy.context.object
    obj.name = name
    obj.rotation_euler = rot
    obj.scale = scale
    if mat:
        obj.data.materials.append(mat)
    obj.select_set(False)
    return obj

def add_cube(name, dims=(1, 1, 1), loc=(0, 0, 0), rot=(0, 0, 0), mat=None):
    bpy.ops.mesh.primitive_cube_add(size=1, location=loc)
    obj = bpy.context.object
    obj.name = name
    obj.scale = dims
    obj.rotation_euler = rot
    if mat:
        obj.data.materials.append(mat)
    obj.select_set(False)
    return obj

def add_bolts(name, circle_radius, count, center, z, mat, bolt_radius=0.006,
              bolt_height=0.005, start_angle=0.0):
    cx, cy, cz = center
    for i in range(count):
        a = start_angle + 2 * math.pi * i / count
        x = cx + circle_radius * math.cos(a)
        y = cy + circle_radius * math.sin(a)
        add_cylinder(f"{name}_{i}", bolt_radius, bolt_height,
                     (x, y, z), mat=mat, verts=6)

# ---------------------------------------------------------------------------
# Topology-optimized strut generation
# ---------------------------------------------------------------------------

def add_meta_ball(meta, co, radius, negative=False):
    e = meta.elements.new()
    e.type = 'BALL'
    e.co = Vector(co)
    e.radius = radius
    e.use_negative = negative

def make_optimized_strut(name, start, end, mat,
                         core_r=0.036, side_r=0.021, spacing=0.027):
    """Generate an organic, topology-optimized structural link using metaballs."""
    mb = bpy.data.metaballs.new(name + '_meta')
    obj = bpy.data.objects.new(name, mb)
    bpy.context.scene.collection.objects.link(obj)
    mb.resolution = 0.24
    mb.threshold = 0.6

    start = Vector(start)
    end = Vector(end)
    axis = end - start
    length = axis.length
    dirn = axis / length

    side1 = Vector((0, 1, 0))
    if abs(dirn.y) > 0.9:
        side1 = Vector((1, 0, 0))
    side2 = dirn.cross(side1).normalized()
    side1 = dirn.cross(side2).normalized()
    if side1.dot(Vector((0, 1, 0))) < 0:
        side1 = -side1

    def path(t):
        p = start + dirn * (length * t)
        p += side2 * (0.020 * math.sin(math.pi * t))
        p += side1 * (0.006 * math.sin(2 * math.pi * t))
        return p

    def fade(t):
        return max(0.0, min(1.0, t / 0.12, (1.0 - t) / 0.12))

    offsets = [
        (Vector((0, 0, 0)), core_r),
        (side1 * 0.036 + side2 * 0.014, side_r),
        (side1 * -0.036 + side2 * 0.014, side_r),
        (side1 * 0.036 - side2 * 0.014, side_r),
        (side1 * -0.036 - side2 * 0.014, side_r),
        (side2 * 0.030, side_r * 0.85),
        (side2 * -0.030, side_r * 0.85),
    ]

    n = max(2, int(length / spacing))
    for i in range(n + 1):
        t = i / n
        w = fade(t)
        c = path(t)
        for off, rad in offsets:
            p = c + off * w
            tr = rad * (0.75 + 0.25 * math.sin(math.pi * t))
            add_meta_ball(mb, p, tr)

    # Cross rungs / brace material, giving the lattice-like cavities.
    nr = max(0, int(length / 0.09) - 1)
    for i in range(1, nr + 1):
        t = i / (nr + 1)
        w = fade(t)
        if w < 0.75:
            continue
        c = path(t)
        r = core_r * 0.7
        add_meta_ball(mb, c, r)
        for off, _ in offsets[1:5]:
            add_meta_ball(mb, c + off * w * 0.5, r)

    add_meta_ball(mb, start, core_r * 1.15)
    add_meta_ball(mb, end, core_r * 1.15)

    # Convert to mesh and apply the metallic material.
    bpy.ops.object.select_all(action='DESELECT')
    obj.select_set(True)
    bpy.context.view_layer.objects.active = obj
    bpy.ops.object.convert(target='MESH')
    obj.name = name
    obj.data.name = name
    obj.data.materials.append(mat)
    bpy.ops.object.shade_smooth()
    obj.select_set(False)
    return obj

def add_cable_curve(name, points, mat, thickness=0.005):
    cu = bpy.data.curves.new(name, 'CURVE')
    cu.dimensions = '3D'
    cu.resolution_u = 20
    cu.bevel_depth = thickness
    cu.bevel_resolution = 5
    cu.use_fill_caps = True
    spline = cu.splines.new('BEZIER')
    spline.bezier_points.add(len(points) - 1)
    for i, p in enumerate(points):
        bp = spline.bezier_points[i]
        bp.co = p
        bp.handle_left_type = 'AUTO'
        bp.handle_right_type = 'AUTO'
    obj = bpy.data.objects.new(name, cu)
    bpy.context.scene.collection.objects.link(obj)
    obj.data.materials.append(mat)
    bpy.ops.object.select_all(action='DESELECT')
    obj.select_set(True)
    bpy.context.view_layer.objects.active = obj
    bpy.ops.object.convert(target='MESH')
    obj.select_set(False)
    return obj

# ---------------------------------------------------------------------------
# Materials
# ---------------------------------------------------------------------------

mat_strut = new_material(
    'TitaniumStrut', hex_rgb('#6f7782'), rough=0.34, metal=0.85, clearcoat=0.2)
mat_al = new_material(
    'MachinedAluminium', hex_rgb('#9aa2ab'), rough=0.22, metal=0.9, clearcoat=0.3)
mat_dark = new_material(
    'DarkAnodized', hex_rgb('#262a30'), rough=0.30, metal=0.7, clearcoat=0.15)
mat_black = new_material(
    'BlackRubber', hex_rgb('#111314'), rough=0.75, metal=0.0)
mat_steel = new_material(
    'PolishedSteel', hex_rgb('#c7ccd2'), rough=0.12, metal=0.95)
mat_brass = new_material(
    'MachinedBrass', hex_rgb('#c99a4b'), rough=0.18, metal=0.9)
mat_desk = new_material(
    'DeskSurface', hex_rgb('#d9d7d0'), rough=0.45, metal=0.0)
mat_backdrop = new_material(
    'StudioBackdrop', hex_rgb('#ecece8'), rough=0.9, metal=0.0)
mat_floor = new_material(
    'StudioFloor', hex_rgb('#5a5c60'), rough=0.55, metal=0.1)
mat_membrane = new_membrane_material(
    'TechnicalMembrane', hex_rgb('#c7d2da'), rough=0.12, trans=0.55, alpha=0.20)
mat_cable = new_material(
    'ControlCable', hex_rgb('#0a0a0c'), rough=0.25, metal=0.1)

# ---------------------------------------------------------------------------
# Build environment
# ---------------------------------------------------------------------------

clear_scene()
bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.samples = 64
bpy.context.scene.cycles.use_denoising = True
bpy.context.scene.render.resolution_x = 1536
bpy.context.scene.render.resolution_y = 1536

world = bpy.data.worlds.new('World')
bpy.context.scene.world = world
world.use_nodes = True
bg_node = world.node_tree.nodes.get('Background')
bg_node.inputs[0].default_value = (0.08, 0.08, 0.09, 1.0)
bg_node.inputs[1].default_value = 0.7

# Desk
add_cube('Desk_Slab', dims=(3.4, 1.7, 0.14), loc=(0.45, 0, -0.07), mat=mat_desk)
add_cube('Desk_Edge_Back', dims=(3.4, 0.06, 0.16), loc=(0.45, -0.82, -0.06), mat=mat_dark)
add_cube('Desk_Edge_Front', dims=(3.4, 0.06, 0.16), loc=(0.45, 0.82, -0.06), mat=mat_dark)

# Floor and backdrop
bpy.ops.mesh.primitive_plane_add(size=1, location=(0.7, -0.2, -0.3))
floor = bpy.context.object
floor.name = 'Floor'
floor.scale = (10, 10, 1)
floor.data.materials.append(mat_floor)
floor.select_set(False)

bpy.ops.mesh.primitive_plane_add(size=1, location=(0.5, 2.7, 1.2))
wall = bpy.context.object
wall.name = 'Backdrop'
wall.rotation_euler = (math.pi / 2, 0, 0)
wall.scale = (9, 6, 1)
wall.data.materials.append(mat_backdrop)
wall.select_set(False)

# Small machined object on the desk, near the gripper.
obj_x = 1.44
add_cylinder('Object_Body', 0.030, 0.070, (obj_x, 0, 0.035), mat=mat_brass, verts=32)
add_cylinder('Object_Top', 0.022, 0.018, (obj_x, 0, 0.075), mat=mat_steel, verts=6)
add_cylinder('Object_Seat', 0.036, 0.004, (obj_x, 0, 0.002), mat=mat_dark, verts=32)

# ---------------------------------------------------------------------------
# Robot assembly
# ---------------------------------------------------------------------------

S = Vector((0.10, 0.0, 0.87))
E = Vector((0.95, 0.0, 1.15))
W = Vector((1.44, 0.0, 0.42))

D1 = (E - S).normalized()
D2 = (W - E).normalized()

# Base -----------------------------------------------------------------
add_cylinder('Base_Plate', 0.170, 0.025, (0, 0, 0.0125), mat=mat_dark, verts=64)
add_cylinder('Base_Flange', 0.135, 0.040, (0, 0, 0.045), mat=mat_al, verts=64)
add_cylinder('Base_Seal', 0.098, 0.008, (0, 0, 0.070), mat=mat_black, verts=64)
add_cylinder('Rotating_Platform', 0.092, 0.045, (0, 0, 0.090), mat=mat_al, verts=64)
add_cylinder('Platform_Ring', 0.098, 0.006, (0, 0, 0.078), mat=mat_dark, verts=64)
add_cylinder('Column_Lower', 0.072, 0.160, (0, 0, 0.185), mat=mat_dark, verts=48)
add_cylinder('Column_Ring', 0.078, 0.020, (0, 0, 0.270), mat=mat_al, verts=48)
add_cylinder('Column_Upper', 0.056, 0.300, (0, 0, 0.420), mat=mat_dark, verts=48)
add_cylinder('Column_Cap', 0.072, 0.025, (0, 0, 0.580), mat=mat_al, verts=48)

add_bolts('Base_Bolt', 0.135, 6, (0, 0), 0.028, mat_black)

# Shoulder joint -------------------------------------------------------
add_cylinder('Shoulder_Housing', 0.090, 0.280, (0, 0, 0.85),
             rot=(math.pi / 2, 0, 0), mat=mat_al, verts=64)
add_cylinder('Shoulder_Ring', 0.095, 0.030, (0, 0, 0.85),
             rot=(math.pi / 2, 0, 0), mat=mat_dark, verts=64)
add_cylinder('Shoulder_AxisPin', 0.010, 0.36, (0, 0, 0.85),
             rot=(math.pi / 2, 0, 0), mat=mat_steel, verts=16)

# Lower optimized strut
lower_start = S
lower_end = E - D1 * 0.10
make_optimized_strut('Lower_Optimized_Strut', lower_start, lower_end,
                     mat_strut, core_r=0.037, side_r=0.022)

# Connection flanges around the optimized strut
flange_loc = S - D1 * 0.04
flange = add_cylinder('Lower_Strut_Flange', 0.058, 0.040, flange_loc, mat=mat_dark)
align_axis(flange, D1)

flange_ring = add_cylinder('Lower_Strut_Flange_Ring', 0.070, 0.010,
                           S + D1 * 0.02, mat=mat_al)
align_axis(flange_ring, D1)

# Elbow joint ----------------------------------------------------------
add_cylinder('Elbow_Housing', 0.085, 0.240, E,
             rot=(math.pi / 2, 0, 0), mat=mat_al, verts=64)
add_cylinder('Elbow_InnerRing', 0.075, 0.050, E,
             rot=(math.pi / 2, 0, 0), mat=mat_dark, verts=64)
add_cylinder('Elbow_AxisPin', 0.009, 0.32, E,
             rot=(math.pi / 2, 0, 0), mat=mat_steel, verts=16)

add_cube('Elbow_Gearbox', dims=(0.15, 0.14, 0.11),
         loc=(E.x + 0.03, 0, E.z - 0.02),
         rot=(0, math.radians(35), 0), mat=mat_dark)

# Forearm optimized strut
forearm_start = E + D2 * 0.12
forearm_end = W - D2 * 0.10
make_optimized_strut('Forearm_Optimized_Strut', forearm_start, forearm_end,
                     mat_strut, core_r=0.030, side_r=0.018, spacing=0.025)

fore_flange = add_cylinder('Forearm_Strut_Flange', 0.050, 0.035,
                           E + D2 * 0.06, mat=mat_al)
align_axis(fore_flange, D2)

fore_ring = add_cylinder('Forearm_Strut_Ring', 0.058, 0.010,
                         E + D2 * 0.10, mat=mat_dark)
align_axis(fore_ring, D2)

# Wrist joint ----------------------------------------------------------
add_cylinder('Wrist_Housing', 0.060, 0.160, W,
             rot=(math.pi / 2, 0, 0), mat=mat_dark, verts=48)
add_cylinder('Wrist_Cap', 0.062, 0.025, (W.x, 0.12, W.z),
             rot=(math.pi / 2, 0, 0), mat=mat_al, verts=48)
add_cylinder('Wrist_Cap', 0.062, 0.025, (W.x, -0.12, W.z),
             rot=(math.pi / 2, 0, 0), mat=mat_al, verts=48)
add_cylinder('Wrist_AxisPin', 0.008, 0.22, W,
             rot=(math.pi / 2, 0, 0), mat=mat_steel, verts=16)

# Gripper interface and two-finger parallel gripper --------------------
wrist_plate = add_cube('Gripper_Interface_Plate', dims=(0.14, 0.10, 0.025),
                       loc=(W.x, 0, W.z - 0.03), mat=mat_al)
add_cube('Gripper_Base', dims=(0.13, 0.12, 0.06),
         loc=(W.x, 0, W.z - 0.12), mat=mat_dark)
add_cylinder('Gripper_GuideRod', 0.010, 0.16, (W.x, 0, W.z - 0.13),
             rot=(math.pi / 2, 0, 0), mat=mat_steel, verts=16)
add_cylinder('Gripper_Actuator', 0.022, 0.12, (W.x, 0, W.z - 0.16),
             rot=(math.pi / 2, 0, 0), mat=mat_al, verts=24)

# Finger assemblies - solid machined bars, no lattice.
finger_y = 0.037
finger_top_z = 0.19
add_cube('Finger_Left', dims=(0.026, 0.024, 0.14),
         loc=(W.x, -finger_y, finger_top_z), mat=mat_steel)
add_cube('Finger_Right', dims=(0.026, 0.024, 0.14),
         loc=(W.x, finger_y, finger_top_z), mat=mat_steel)

# Small replaceable contact pads
add_cube('Contact_Pad_Left', dims=(0.016, 0.014, 0.030),
         loc=(W.x, -finger_y, 0.055), mat=mat_black)
add_cube('Contact_Pad_Right', dims=(0.016, 0.014, 0.030),
         loc=(W.x, finger_y, 0.055), mat=mat_black)

# Visible fasteners on gripper
add_cylinder('Gripper_Bolt_L', 0.006, 0.004, (W.x, -finger_y + 0.006, 0.225),
             mat=mat_black, verts=6)
add_cylinder('Gripper_Bolt_R', 0.006, 0.004, (W.x, finger_y - 0.006, 0.225),
             mat=mat_black, verts=6)

# Restricted membrane panel on one optimized strut ----------------------
bpy.ops.mesh.primitive_grid_add(size=1, location=(0.55, 0, 0.98),
                                x_subdivisions=32, y_subdivisions=8)
mem = bpy.context.object
mem.name = 'Membrane_Lower'
mem.rotation_euler = (math.pi / 2, math.radians(-18), 0)
mem.scale = (0.95, 0.11, 1)
mem.data.materials.append(mat_membrane)
mem.select_set(False)

# Controlled cable routing near the mechanical spine --------------------
cable_points = [
    (0.04, 0.075, 0.38),
    (0.06, 0.085, 0.72),
    (0.12, 0.090, 0.88),
    (0.50, 0.080, 1.09),
    (0.90, 0.065, 1.12),
    (1.20, 0.045, 0.72),
    (1.42, 0.030, 0.46),
]
add_cable_curve('Control_Cable', cable_points, mat_cable, thickness=0.005)

# ---------------------------------------------------------------------------
# Lighting and camera
# ---------------------------------------------------------------------------

# Soft key light
bpy.ops.object.light_add(type='AREA', location=(2.2, -1.7, 2.6))
key = bpy.context.object
key.name = 'Key_Light'
key.data.energy = 220
key.data.size = 1.8
aim_obj(key, (0.9, 0, 0.65))
key.select_set(False)

# Fill light from the opposite side
bpy.ops.object.light_add(type='AREA', location=(-0.6, 1.3, 1.8))
fill = bpy.context.object
fill.name = 'Fill_Light'
fill.data.energy = 70
fill.data.size = 1.5
aim_obj(fill, (0.9, 0, 0.6))
fill.select_set(False)

# Rim / accent light
bpy.ops.object.light_add(type='AREA', location=(2.8, 1.8, 1.6))
rim = bpy.context.object
rim.name = 'Rim_Light'
rim.data.energy = 55
rim.data.size = 1.2
aim_obj(rim, (0.8, 0, 0.8))
rim.select_set(False)

# Camera
cam_data = bpy.data.cameras.new('StudioCamera')
cam = bpy.data.objects.new('StudioCamera', cam_data)
bpy.context.scene.collection.objects.link(cam)
bpy.context.scene.camera = cam
cam.location = (1.75, -2.55, 1.85)
cam.data.lens = 50
aim_obj(cam, (0.85, 0, 0.62))
cam.select_set(False)

# Make sure the viewport / export sees the final assembly.
bpy.ops.object.select_all(action='DESELECT')
bpy.context.view_layer.update()
```