"""Sliding pressure door for the vessel's main entrance (models/pressure-door-sliding), after the user's two reference renders and the video Assets/0080.mp4 (2026-10-15): a double-leaf sliding door in a rounded-rectangle bezel with white light strips, a round lock escutcheon on the seam with a light ring (red locked, amber, green unlocked) and a turning handle disc, a grab rail at each side, a sign over the door, a camera, a console on a swing arm at the left and a service column at the right. The user: a PRESSURE door, not an airlock; it competes with the hinged door (models/pressure-door) for the same gate, so it is a stand-alone module like that one. Second version (the user's corrections of 2026-10-15, with two marked-up pictures: "delete those cables on top, and fix the door this is not the same door, handle should rotate examine the video all the pictures carefully"): * no cables along the top; * the opening is squatter, as the reference (2.10 m high instead of 2.20; 1.40 m wide: the ship's gate allows no more); * the leaves carry the reference's relief: a raised field, a wide chevron band on the left with a vertical jog narrowing into the escutcheon, a wedge opening out to the right, the keypad in the chevron, both small hatches on the left leaf; * the seam runs straight down the centre through the escutcheon (the video, frames 43-46: the ring light parts with the leaves, the handle bar stays with the left leaf); * the handle is a disc with a bar across it, on the left leaf, overhanging the right leaf's half of the well: it turns a quarter clockwise while the ring changes colour and stays there, bar across the band when shut, along it when open (the user's two marked pictures 'CLOSED' / 'OPEN' of 2026-10-15; the video's brief wobble was read first and dropped). Built headless (another Blender instance, not the MCP's): blender.exe -b --factory-startup --python sd_run.py -- build Frame: metres, Z up, floor at z = 0. The plain wall's front face is y = 0; the front is -Y (the side the references show); +X is right seen from the front. Door axis at x = 0, the opening's centre at z = ZC. How it moves (separate objects, origin on the part; see sd_rig.py): SM_PDSliding_LeafL / LeafR slide TRAVEL along -X / +X into pockets inside the door plate (a slot y in [Y_SF, Y_SB]) SM_PDSliding_Handle the handle disc (child of the left leaf): turns about the door axis M_PDS_Lock (material) the ring's light: red -> amber -> green as the slider goes 0 -> 0.30 SM_PDSliding_Monitor console (monitor, arm, tray) yaws about its post SM_PDSliding_Camera the camera yaws on its ball The leaves are 0.04 m larger than the opening all round (the rebate behind the dark lip) and run in a 0.105 m slot through the plate; nothing on a leaf stands closer than 1.1 cm to the slot's walls (the bar is the proudest part, 4.9 cm in front of the plain wall). The relief has 45 degree chamfers (slab rounding with one segment), as the reference. """ import bpy, bmesh, math, os from mathutils import Vector, Matrix BUILD = r"D:\HADAL - The Descent\models\pressure-door-sliding\build" exec(open(os.path.join(BUILD, "sd_lib.py"), encoding="utf-8").read()) # ------------------------------------------------------------------ dimensions (metres) OW, OH = 0.70, 1.05 # opening half-width, half-height (1.40 x 2.10 clear: the ship's gate is 3.30 m between its wall # pieces, and with the handle's overhang the pockets fit that only at 1.40) OZ0 = 0.16 # opening bottom (sill) ZC = OZ0 + OH # 1.21: the opening's centre, the lock's height OR = 0.32 # opening corner radius REB = 0.04 # leaf rebate behind the lip SEG = 8 # points per corner on the lofted loops Y_LF, Y_LB = -0.030, 0.040 # leaf front / back faces Y_SF, Y_SB = -0.060, 0.050 # the slot's front / back walls (the front one 6 cm in, for the leaf relief) Y_LIP, Y_BEZ = -0.095, -0.125 # dark lip face, bezel front Y_PAN, Y_PLATE = -0.087, -0.075 # surround panel face, door plate face Y_WB = 0.30 # wall back Y_COL = -0.045 # the side columns' face LIP_W, BEZ_W = 0.06, 0.18 # the dark lip band and the bezel, as offsets from the opening PX, PZ0, PZ1 = 1.645, 0.0, 3.00 # door plate: x +-PX, z PZ0..PZ1 (the pockets; 5 mm inside the ship's gate opening, +-1.650) SLOT_X, SLOT_Z0, SLOT_Z1 = 1.615, OZ0 - REB - 0.01, OZ0 + 2 * OH + REB + 0.01 TRAVEL = 0.845 # each leaf's slide (the left leaf's handle disc, 12.5 cm over the seam, must clear the jamb too) SEAM = 0.002 # half the gap between the leaves COL_W = 0.78 # the side columns LX, RX = -(PX + COL_W / 2), PX + COL_W / 2 # column centres # the leaf relief, above the field (level A) : band (level B), escutcheon, handle Y_FLOOR, Y_FIELD, Y_BAND, Y_ESC = -0.018, -0.036, -0.044, -0.045 # 6 mm field, 8 mm band, the escutcheon 1 mm over it MARGIN = 0.05 # flat margin round the field, inside the opening's edge R_ESC, R_RIM_OUT, R_RIM_IN, R_WELL, R_DISC = 0.24, 0.18, 0.15, 0.14, 0.125 BAR_ANG = math.radians(45.0) # the bar when SHUT: lower left to upper right, across the band (the user's picture 'CLOSED'); # open it has turned a quarter clockwise, along the band (picture 'OPEN') P_LEAF = {-1: Vector((-0.395, 0.005, ZC)), 1: Vector((0.395, 0.005, ZC))} # leaf pivots P_HANDLE = Vector((0.0, -0.028, ZC)) # the handle's axis (world, shut) P_MON = Vector((LX, -0.18, 1.26)) # console post axis (the swing pivot) P_CAM = Vector((0.85, -0.16, 2.80)) # camera ball MON_TILT = math.radians(8.0) CAM_PITCH = math.radians(20.0) SCREENS = {} # name -> (centre, u axis, v axis, width, height, atlas region), for sd_surfaces MAT_NAMES = ["M_PDS_Paint", "M_PDS_Dark", "M_PDS_Steel", "M_PDS_Rubber", "M_PDS_CableRed", "M_PDS_Orange", "M_PDS_Light", "M_PDS_Lock", "M_PDS_LampRed", "M_PDS_LampGreen", "M_PDS_Screen", "M_PDS_SignText", "M_PDS_Glass"] PAINT, DARK, STEEL, RUBBER, CRED, ORANGE, LIGHT, LOCK, LRED, LGREEN, SCREEN, SIGNTXT, GLASS = range(len(MAT_NAMES)) PREVIEW = {"M_PDS_Paint": ((0.46, 0.47, 0.48), 0.0, 0.45), "M_PDS_Dark": ((0.035, 0.037, 0.04), 0.3, 0.5), "M_PDS_Steel": ((0.62, 0.63, 0.64), 1.0, 0.25), "M_PDS_Rubber": ((0.02, 0.02, 0.022), 0.0, 0.8), "M_PDS_CableRed": ((0.30, 0.03, 0.02), 0.0, 0.7), "M_PDS_Orange": ((0.80, 0.30, 0.05), 0.0, 0.45), "M_PDS_Light": ((1.0, 1.0, 1.0), 0.0, 0.3), "M_PDS_Lock": ((1.0, 0.1, 0.05), 0.0, 0.3), "M_PDS_LampRed": ((0.9, 0.05, 0.03), 0.0, 0.3), "M_PDS_LampGreen": ((0.05, 0.8, 0.2), 0.0, 0.3), "M_PDS_Screen": ((0.03, 0.15, 0.18), 0.0, 0.2), "M_PDS_SignText": ((0.2, 0.9, 1.0), 0.0, 0.3), "M_PDS_Glass": ((0.6, 0.7, 0.75), 0.0, 0.05)} GLOW = {"M_PDS_Light": ((1.0, 0.97, 0.92), 12.0), "M_PDS_LampRed": ((1.0, 0.06, 0.03), 2.5), "M_PDS_LampGreen": ((0.08, 1.0, 0.25), 2.5), "M_PDS_SignText": ((0.25, 0.95, 1.0), 3.0)} def get_mats(): out = [] for n in MAT_NAMES: m = bpy.data.materials.get(n) if m is None: m = bpy.data.materials.new(n) m.use_nodes = True b = m.node_tree.nodes.get("Principled BSDF") col, met, rough = PREVIEW[n] b.inputs["Base Color"].default_value = (*col, 1) b.inputs["Metallic"].default_value = met b.inputs["Roughness"].default_value = rough m.diffuse_color = (*col, 1) if n in GLOW: b.inputs["Emission Color"].default_value = (*GLOW[n][0], 1) b.inputs["Emission Strength"].default_value = GLOW[n][1] b.inputs["Base Color"].default_value = (0.03, 0.03, 0.03, 1) if n == "M_PDS_Lock": lock_nodes(m) out.append(m) return out def lock_nodes(m): """the ring light's colour comes from a ramp (red, amber, green) whose Fac the rig drives from the open slider""" nt = m.node_tree b = nt.nodes.get("Principled BSDF") ramp = nt.nodes.get("LockRamp") or nt.nodes.new('ShaderNodeValToRGB') ramp.name = ramp.label = "LockRamp" ramp.location = (-400, 0) cr = ramp.color_ramp cr.interpolation = 'LINEAR' while len(cr.elements) > 2: cr.elements.remove(cr.elements[-1]) cr.elements[0].position, cr.elements[0].color = 0.0, (1.0, 0.03, 0.02, 1) cr.elements[1].position, cr.elements[1].color = 1.0, (0.08, 1.0, 0.18, 1) mid = cr.elements.new(0.5) mid.color = (1.0, 0.45, 0.04, 1) ramp.inputs[0].default_value = 0.0 nt.links.new(ramp.outputs["Color"], b.inputs["Emission Color"]) b.inputs["Emission Strength"].default_value = 2.5 b.inputs["Base Color"].default_value = (0.03, 0.03, 0.03, 1) b.inputs["Roughness"].default_value = 0.3 return ramp def coll(name, parent=None): c = bpy.data.collections.get(name) if c is None: c = bpy.data.collections.new(name) (parent or bpy.context.scene.collection).children.link(c) return c def add_mesh(bm, me, M, mat): v0, f0 = len(bm.verts), len(bm.faces) bm.from_mesh(me) bm.verts.ensure_lookup_table() bm.faces.ensure_lookup_table() for i in range(v0, len(bm.verts)): bm.verts[i].co = M @ bm.verts[i].co for i in range(f0, len(bm.faces)): bm.faces[i].material_index = mat def text_mesh(body, size, extrude): cu = bpy.data.curves.new("sd_tmp_text", 'FONT') cu.body = body cu.size = size cu.extrude = extrude cu.align_x = 'CENTER' cu.align_y = 'CENTER' cu.resolution_u = 3 ob = bpy.data.objects.new("sd_tmp_text", cu) bpy.context.scene.collection.objects.link(ob) dg = bpy.context.evaluated_depsgraph_get() me = bpy.data.meshes.new_from_object(ob.evaluated_get(dg)) bpy.data.objects.remove(ob) bpy.data.curves.remove(cu) return me def text_at(bm, body, size, x, y, z, mat, extrude=0.0006): """lettering standing on a face at y, `extrude` proud, facing -Y (text in XZ: rotate the XY text onto XZ)""" me = text_mesh(body, size, extrude) M = Matrix.Translation((x, y - extrude, z)) @ Matrix.Rotation(math.pi / 2, 4, 'X') add_mesh(bm, me, M, mat) bpy.data.meshes.remove(me) def bolt_head(bm, M, r=0.016, mat=STEEL, n=10): """socket cap screw facing local -Y""" p = [(r, 0.003), (r, -0.008), (r * 0.75, -0.013), (r * 0.35, -0.013), (r * 0.35, -0.010), (0.0, -0.010)] lathe(bm, p, n, axis='Y', mat=mat, M=M) def ball(bm, c, r, mat, n=12): prof = [(r * math.sin(a), r * math.cos(a)) for a in [math.pi * k / 8 for k in range(9)]] lathe(bm, prof, n, center=c, axis='Y', mat=mat) def dome_lamp(bm, c, mat_lens, r=0.022): """a round lamp facing -Y at c: dark bezel ring and a domed lens""" lathe(bm, [(r + 0.008, 0.0), (r + 0.008, -0.012), (r + 0.002, -0.016), (r, -0.016)], 16, center=c, axis='Y', mat=DARK) lathe(bm, [(r, -0.016), (r * 0.72, -0.024), (0.0, -0.028)], 16, center=c, axis='Y', mat=mat_lens) def op_loop(d, seg=SEG): """the opening's outline offset outward by d""" return rr_loop(0.0, ZC, OW + d, OH + d, OR + d, seg) def pan_loop(d, seg=SEG): """the surround panel's outline (x +-(PX - 0.08), z 0.06..2.96) offset by d""" return rr_loop(0.0, 1.51, PX - 0.08 + d, 1.45 + d, 0.10 + d, seg) # ================================================================== FRAME (static): door plate, bezel, rails, sign def build_frame(mats, C): bm = bmesh.new() r = 0.012 L0 = op_loop(0.0) # --- front body: reveal, dark lip, bezel, surround panel, plate skin; its back face is the slot's front wall lv = [(L0, Y_SF, None), (L0, Y_LIP, 'in'), (op_loop(LIP_W), Y_LIP, 'front'), (op_loop(LIP_W), Y_BEZ, 'in'), (op_loop(BEZ_W - r), Y_BEZ, 'front')] lv += round_levels(op_loop, BEZ_W, Y_BEZ, Y_PAN, r) lv += [(op_loop(BEZ_W), Y_PAN, 'out'), (pan_loop(-r), Y_PAN, 'front')] lv += round_levels(pan_loop, 0.0, Y_PAN, Y_PLATE, r) RECT = rect_from_loop(pan_loop(0.0), 0.0, 1.51, -PX, PX, PZ0, PZ1) lv += [(pan_loop(0.0), Y_PLATE, 'out'), (RECT, Y_PLATE, 'front'), (RECT, Y_SF, 'out'), (L0, Y_SF, 'back')] mb = [DARK, DARK] + [PAINT] * (len(lv) - 4) + [DARK] loft(bm, lv, mats=mb, center=(0.0, ZC)) # --- spacer round the slot (the pockets' top, bottom and ends), between the front and back bodies O = rect_poly(0.0, (PZ0 + PZ1) / 2, PX, (PZ1 - PZ0) / 2) S = rect_poly(0.0, (SLOT_Z0 + SLOT_Z1) / 2, SLOT_X, (SLOT_Z1 - SLOT_Z0) / 2) loft(bm, [(S, Y_SF, None), (O, Y_SF, 'front'), (O, Y_SB, 'out'), (S, Y_SB, 'back'), (S, Y_SF, 'in')], mats=[PAINT, PAINT, PAINT, DARK], center=(0.0, ZC)) # --- back body: the slot's back wall, the reveal through the rest of the wall, the ship-side face with a back bezel RB = rect_from_loop(L0, 0.0, ZC, -PX, PX, PZ0, PZ1) loft(bm, [(L0, Y_SB, None), (RB, Y_SB, 'front'), (RB, Y_WB, 'out'), (op_loop(0.08), Y_WB, 'back'), (op_loop(0.08), Y_WB + 0.03, 'out'), (op_loop(0.0), Y_WB + 0.03, 'back'), (L0, Y_SB, 'in')], mats=[DARK, PAINT, PAINT, PAINT, PAINT, DARK], center=(0.0, ZC)) # --- light strips on the dark lip, top and bottom for z in (OZ0 + 2 * OH + LIP_W / 2, OZ0 - LIP_W / 2): box_at(bm, (0.0, Y_LIP - 0.002, z), (0.60, 0.004, 0.022), r=0.0015, seg=1, mat=LIGHT) # --- grab rails on the surround panel, each on three brackets for sx in (-1, 1): x = sx * (OW + BEZ_W + 0.10) tube(bm, [(x, -0.145, 0.50), (x, -0.145, 2.02)], 0.017, 14, caps=('dome', 'dome'), mat=DARK) for z in (0.56, 1.26, 1.96): box_at(bm, (x, -0.110, z), (0.05, 0.05, 0.04), r=0.004, mat=STEEL) # --- sign over the door: a rounded bezel, a dark screen, lettering poly_slab(bm, rr_loop(0.0, 2.74, 0.34, 0.09, 0.04, 6), Y_PAN, Y_PAN - 0.014, r=0.006, mat=PAINT) box_at(bm, (0.0, Y_PAN - 0.016, 2.74), (0.60, 0.004, 0.12), r=0.002, seg=1, mat=SCREEN) SCREENS["sign"] = ((0.0, Y_PAN - 0.018, 2.74), (1, 0, 0), (0, 0, 1), 0.60, 0.12, "sign") text_at(bm, "PRESSURE DOOR", 0.058, 0.0, Y_PAN - 0.018, 2.74, SIGNTXT) # --- screws on the surround panel's corners and the bezel's shoulder sx = PX - 0.14 for (x, z) in ((-sx, 0.14), (sx, 0.14), (-sx, 2.88), (sx, 2.88), (-sx, 1.51), (sx, 1.51), (-0.85, 2.66)): bolt_head(bm, Matrix.Translation((x, Y_PAN, z)), 0.011, STEEL) # --- camera bracket: a block on the panel, a stub and the ball the camera turns on box_at(bm, (P_CAM.x, Y_PAN - 0.02, P_CAM.z), (0.06, 0.04, 0.06), r=0.004, mat=PAINT) tube(bm, [(P_CAM.x, Y_PAN - 0.035, P_CAM.z), (P_CAM.x, P_CAM.y, P_CAM.z)], 0.010, 10, caps=(None, None), mat=STEEL) ball(bm, P_CAM, 0.022, STEEL) return finish(bm, "SM_PDSliding_Frame", C, mats) # ================================================================== WALL (static): columns, trough, console mount, floor cable def build_wall(mats, C): bm = bmesh.new() for cx in (LX, RX): box_at(bm, (cx, (Y_COL + Y_WB) / 2, 1.50), (COL_W, Y_WB - Y_COL, 3.00), r=0.008, mat=PAINT) W = 2 * (PX + COL_W) box_at(bm, (0.0, 0.18, 3.20), (W, 0.24, 0.40), r=0.006, mat=DARK) # dark recess above the wall (no cables: the user) # --- right column: service hatch, four lamps, warning / vent panel, floor grille cx = RX poly_slab(bm, rr_loop(cx, 2.50, 0.28, 0.12, 0.03, 6), Y_COL, Y_COL - 0.020, r=0.006, mat=PAINT) box_at(bm, (cx - 0.11, Y_COL - 0.021, 2.50), (0.10, 0.004, 0.03), r=0.002, seg=1, mat=DARK) for (dx, dz) in ((-0.24, -0.08), (0.24, -0.08), (-0.24, 0.08), (0.24, 0.08)): bolt_head(bm, Matrix.Translation((cx + dx, Y_COL - 0.020, 2.50 + dz)), 0.009, STEEL) poly_slab(bm, rr_loop(cx, 2.10, 0.30, 0.07, 0.03, 6), Y_COL, Y_COL - 0.012, r=0.005, mat=PAINT) for dx in (-0.21, -0.07, 0.07, 0.21): dome_lamp(bm, (cx + dx, Y_COL - 0.012, 2.10), LRED) poly_slab(bm, rr_loop(cx, 1.42, 0.25, 0.23, 0.03, 6), Y_COL, Y_COL - 0.016, r=0.006, mat=PAINT) box_at(bm, (cx - 0.12, Y_COL - 0.0175, 1.42), (0.16, 0.003, 0.30), r=0.001, seg=1, mat=DARK) for k in range(9): box_at(bm, (cx - 0.12, Y_COL - 0.021, 1.42 + (k - 4) * 0.032), (0.14, 0.005, 0.012), r=0.002, seg=1, mat=PAINT) box_at(bm, (cx + 0.11, Y_COL - 0.0175, 1.52), (0.14, 0.003, 0.09), r=0.001, seg=1, mat=DARK) box_at(bm, (cx + 0.11, Y_COL - 0.0195, 1.555), (0.14, 0.002, 0.012), r=0.001, seg=1, mat=ORANGE) box_at(bm, (cx, Y_COL - 0.004, 0.30), (0.66, 0.008, 0.42), r=0.002, seg=1, mat=DARK) for k in range(11): box_at(bm, (cx, Y_COL - 0.011, 0.30 + (k - 5) * 0.036), (0.62, 0.006, 0.018), r=0.002, seg=1, mat=PAINT) # --- left column: conduits with clamp straps, the console's wall bracket and clamp ring, junction box, floor cable for k in range(5): x = LX - 0.31 + 0.034 * k path = fillet3([(x, -0.080, 0.42), (x, -0.080, 3.10), (x, 0.12, 3.26)], 0.06, 4) tube(bm, path, 0.014, 10, caps=('flat', 'dome'), mat=PAINT if k % 2 == 0 else DARK) for z in (0.95, 1.85, 2.75): box_at(bm, (LX - 0.2425, -0.101, z), (0.235, 0.010, 0.022), r=0.002, seg=1, mat=STEEL) for x in (LX - 0.345, LX - 0.14): box_at(bm, (x, -0.075, z), (0.03, 0.06, 0.022), r=0.002, seg=1, mat=STEEL) box_at(bm, (P_MON.x, -0.090, P_MON.z), (0.18, 0.09, 0.22), r=0.006, mat=PAINT) tube(bm, [(P_MON.x, -0.13, P_MON.z), (P_MON.x, P_MON.y + 0.024, P_MON.z)], 0.016, 12, caps=(None, None), mat=STEEL) lathe(bm, [(0.028, P_MON.z + 0.04), (0.028, P_MON.z - 0.04), (0.0195, P_MON.z - 0.04), (0.0195, P_MON.z + 0.04), (0.028, P_MON.z + 0.04)], 20, center=(P_MON.x, P_MON.y, 0.0), axis='Z', mat=STEEL) box_at(bm, (LX - 0.17, -0.075, 0.30), (0.34, 0.06, 0.26), r=0.006, mat=PAINT) for k, m in enumerate((LRED, LRED, LGREEN, LRED)): lathe(bm, [(0.006, 0.0), (0.0045, -0.004), (0.0, -0.0055)], 8, center=(LX - 0.29 + 0.03 * k, -0.105, 0.36), axis='Y', mat=m) sx = LX - 0.06 box_at(bm, (sx, -0.112, 0.235), (0.04, 0.014, 0.03), r=0.002, seg=1, mat=DARK) path = fillet3([(sx, -0.115, 0.235), (sx, -0.20, 0.17), (sx + 0.02, -0.30, 0.012), (sx + 0.55, -0.42, 0.010), (-0.30, -0.47, 0.010), (0.90, -0.42, 0.010), (RX - 0.1, -0.30, 0.010), (RX + 0.39, -0.26, 0.010)], 0.08, 4) tube(bm, path, 0.009, 10, caps=('flat', 'dome'), mat=RUBBER) return finish(bm, "SM_PDSliding_Wall", C, mats) # ================================================================== LEAVES (moving) def leaf_outline(side): return clip_poly(rr_loop(0.0, ZC, OW + REB, OH + REB, OR + REB, SEG), halfplane(side, side * SEAM)) def field_outline(side, gap=0.012): """the raised field: the opening inset by MARGIN, cut at the seam""" return clip_poly(rr_loop(0.0, ZC, OW - MARGIN, OH - MARGIN, OR - MARGIN, SEG), halfplane(side, side * gap)) def uv(u, v): """door coordinates: u right of the seam as a fraction of the opening's half-width, v above the centre as a fraction of its half-height (the reference pictures were measured that way)""" return Vector((u * OW, ZC + v * OH)) def chevron_poly(): """the left leaf's raised band: a wide chevron from the left jamb narrowing into the escutcheon, its upper edge with a vertical jog (the reference: the band's edge comes down from the top left, drops vertically, then runs on to the lock)""" return [uv(-1.0, -0.25), uv(-0.33, 0.03), uv(-0.31, 0.06), uv(-0.56, 0.19), uv(-0.56, 0.54), uv(-1.0, 0.80)] def wedge_poly(): """the right leaf's raised band: a wedge from the escutcheon opening out to the right jamb""" return [uv(0.32, -0.12), uv(1.0, -0.47), uv(1.0, 0.55), uv(0.26, 0.33)] def band_piece(side): poly = chevron_poly() if side < 0 else wedge_poly() return clip_poly(clip_poly(poly, field_outline(side)), halfplane(side, side * 0.012)) def escutcheon(bm, side, a0, a1): """half of the round lock escutcheon: the raised plate, two rims with the light channel between them, the well's wall. Profile from the outside in (the outside on the left of travel).""" yA, yB = Y_FIELD, Y_ESC prof = [(R_ESC, yA), (R_ESC, yB + 0.006), (R_ESC - 0.006, yB), (0.21, yB), (0.21, yB + 0.002), (R_RIM_OUT + 0.008, yB + 0.002), (R_RIM_OUT + 0.008, yB + 0.004), (R_RIM_OUT, yB + 0.004), (R_RIM_OUT, yB - 0.002), (R_RIM_OUT - 0.005, yB - 0.002), (R_RIM_OUT - 0.005, yA), (R_RIM_IN + 0.005, yA), (R_RIM_IN + 0.005, yB - 0.002), (R_RIM_IN, yB - 0.002), (R_RIM_IN, yB + 0.004), (R_WELL - 0.005, yB + 0.004), (R_WELL - 0.005, Y_FLOOR)] _, rows = lathe(bm, prof, 28, center=(0.0, 0.0, ZC), axis='Y', a0=a0, a1=a1, mat=PAINT) for col in (0, -1): face(bm, [rows[i][col] for i in range(len(prof))], PAINT, out=Vector((-side, 0, 0))) # the light channel's floor, 0.5 mm proud of the channel lathe(bm, [(R_RIM_OUT - 0.007, yA - 0.0005), (R_RIM_IN + 0.007, yA - 0.0005)], 28, center=(0.0, 0.0, ZC), axis='Y', a0=a0, a1=a1, mat=LOCK) # the well's dark floor (seen round the handle disc) lathe(bm, [(R_WELL - 0.006, Y_FLOOR - 0.0004), (0.0, Y_FLOOR - 0.0004)], 28, center=(side * 0.0025, 0.0, ZC), axis='Y', a0=a0, a1=a1, mat=DARK) def hatch(bm, cx, cz, w, h, mat_line=DARK): """a small service hatch on the field: a rounded plate with a chamfered rim and a dark outline line""" poly_slab(bm, rr_loop(cx, cz, w / 2, h / 2, 0.025, 4), Y_FIELD, Y_FIELD - 0.004, r=0.003, mat=PAINT) poly_slab(bm, rr_loop(cx, cz, w / 2 - 0.012, h / 2 - 0.012, 0.016, 4), Y_FIELD - 0.004, Y_FIELD - 0.0055, r=0.001, mat=mat_line) poly_slab(bm, rr_loop(cx, cz, w / 2 - 0.016, h / 2 - 0.016, 0.013, 4), Y_FIELD - 0.0055, Y_FIELD - 0.0075, r=0.001, mat=PAINT) def build_leaf(side, mats, C): bm = bmesh.new() base = leaf_outline(side) poly_slab(bm, base, Y_FLOOR, Y_LB, r=0.010, mat=PAINT) # back layer, rounded back edge; its front is the well's floor poly_slab(bm, notch_edge(base, side * SEAM, ZC, R_WELL, side), Y_FLOOR, Y_LF, r=0.006, mat=PAINT) # front layer with the well's notch poly_slab(bm, notch_edge(field_outline(side), side * 0.012, ZC, R_WELL, side), Y_LF, Y_FIELD, r=0.005, seg=1, mat=PAINT) # raised field (level A), 5 mm chamfer poly_slab(bm, band_piece(side), Y_FIELD, Y_BAND, r=0.007, seg=1, mat=PAINT) # the band (level B): chevron / wedge, 7 mm chamfer delta = math.asin(0.003 / R_ESC) a0, a1 = (math.pi / 2 + delta, 3 * math.pi / 2 - delta) if side < 0 else (-math.pi / 2 + delta, math.pi / 2 - delta) escutcheon(bm, side, a0, a1) # markings and small parts z_marks, z_slots = ZC + 0.92 * OH, ZC + 0.85 * OH if side < 0: text_at(bm, "< < <", 0.034, -0.30 * OW, Y_FIELD, z_marks, ORANGE) box_at(bm, (-0.33 * OW, Y_FIELD - 0.002, z_slots), (0.09, 0.004, 0.016), r=0.002, seg=1, mat=DARK) kp = uv(-0.81, 0.36) box_at(bm, (kp.x, Y_BAND - 0.002, kp.y), (0.19, 0.004, 0.21), r=0.006, seg=2, mat=DARK) box_at(bm, (kp.x + 0.02, Y_BAND - 0.0045, kp.y), (0.13, 0.001, 0.17), r=0.0005, seg=1, mat=SCREEN) SCREENS["keypad"] = ((kp.x + 0.02, Y_BAND - 0.005, kp.y), (1, 0, 0), (0, 0, 1), 0.13, 0.17, "keypad") box_at(bm, (kp.x - 0.072, Y_BAND - 0.0045, kp.y), (0.022, 0.001, 0.17), r=0.0005, seg=1, mat=LOCK) # the "CLOSE" strip, lit for (u, v) in ((-0.39, -0.82), (-0.15, -0.82)): h = uv(u, v) hatch(bm, h.x, h.y, 0.19, 0.27) sl = uv(-0.07, -0.43) box_at(bm, (sl.x, Y_FIELD - 0.002, sl.y), (0.09, 0.004, 0.016), r=0.002, seg=1, mat=DARK) else: text_at(bm, "> > >", 0.034, 0.30 * OW, Y_FIELD, z_marks, ORANGE) box_at(bm, (0.33 * OW, Y_FIELD - 0.002, z_slots), (0.09, 0.004, 0.016), r=0.002, seg=1, mat=DARK) t = uv(0.36, 0.76) text_at(bm, "PRESSURE DOOR", 0.032, t.x, Y_FIELD, t.y, ORANGE) t = uv(0.36, 0.56) text_at(bm, "01", 0.17, t.x, Y_FIELD, t.y, ORANGE) for (u, v, rot) in ((0.42, 0.40, 1), (0.42, -0.22, -1)): # the two orange arcs round the lock, as in the reference c = uv(u, v) lathe(bm, [(0.03, Y_BAND - 0.0004), (0.022, Y_BAND - 0.0004)], 10, center=(c.x, 0.0, c.y), axis='Y', a0=math.radians(20 if rot > 0 else 200), a1=math.radians(110 if rot > 0 else 290), mat=ORANGE) name = "SM_PDSliding_LeafL" if side < 0 else "SM_PDSliding_LeafR" ob = finish(bm, name, C, mats, origin=P_LEAF[side]) ob["closed_location"] = list(ob.location) ob["slide_x"] = side * TRAVEL return ob # ================================================================== HANDLE (moving, child of the left leaf) def build_handle(mats, C, leaf): """the handle disc: a rim, a recessed face, the bar across it (built shut: lower left to upper right). It sits in the well across both leaves and turns a quarter clockwise about the door axis to open (sd_rig).""" bm = bmesh.new() yF = Y_FLOOR - 0.0016 # 1.6 mm in front of the well's floor (the front is -Y) prof = [(R_DISC, yF), (R_DISC, Y_ESC + 0.0035), (R_DISC - 0.0035, Y_ESC), (R_DISC - 0.017, Y_ESC), (R_DISC - 0.017, Y_ESC + 0.005), (0.0, Y_ESC + 0.005)] lathe(bm, prof, 32, center=(0.0, 0.0, ZC), axis='Y', mat=PAINT) lathe(bm, [(0.0, yF), (R_DISC, yF)], 32, center=(0.0, 0.0, ZC), axis='Y', mat=PAINT) # the back (closes the disc) half = R_DISC - 0.020 rect = [Vector((-half, -0.018)), Vector((half, -0.018)), Vector((half, 0.018)), Vector((-half, 0.018))] bar = [Vector((0.0, ZC)) + Matrix.Rotation(BAR_ANG, 2) @ p for p in rect] poly_slab(bm, bar, Y_ESC + 0.005, Y_ESC - 0.004, r=0.003, seg=1, mat=PAINT) # the bar, 4 mm proud of the rim ob = finish(bm, "SM_PDSliding_Handle", C, mats, origin=P_HANDLE) ob.parent = leaf ob.matrix_parent_inverse = Matrix.Identity(4) ob.location = P_HANDLE - leaf.location ob["closed_rotation"] = [0.0, 0.0, 0.0] return ob # ================================================================== CONSOLE (moving): post, neck, monitor, tray def build_monitor(mats, C): bm = bmesh.new() px, py, pz = P_MON tube(bm, [(px, py, 1.10), (px, py, 1.42)], 0.017, 14, caps=('dome', 'dome'), mat=STEEL) tube(bm, fillet3([(px, py, 1.40), (px, py, 1.49), (px, py - 0.085, 1.50)], 0.04, 4), 0.014, 12, caps=(None, 'dome'), mat=STEEL) R = Matrix.Rotation(MON_TILT, 4, 'X') c = Vector((px, py - 0.105, 1.52)) box_at(bm, c, (0.50, 0.03, 0.34), r=0.008, seg=2, mat=DARK, rot=R) sc = c + R.to_3x3() @ Vector((0, -0.0165, 0)) box_at(bm, sc, (0.46, 0.004, 0.30), r=0.0008, seg=1, mat=SCREEN, rot=R) u, v = Vector((1, 0, 0)), R.to_3x3() @ Vector((0, 0, 1)) SCREENS["monitor"] = (tuple(sc + R.to_3x3() @ Vector((0, -0.002, 0))), tuple(u), tuple(v), 0.46, 0.30, "monitor") box_at(bm, c + R.to_3x3() @ Vector((0, 0, -0.19)), (0.08, 0.03, 0.04), r=0.004, mat=DARK, rot=R) # the monitor's chin tube(bm, fillet3([(px, py, 1.12), (px, py - 0.06, 1.12), (px, py - 0.13, 1.09)], 0.03, 4), 0.012, 10, caps=(None, 'dome'), mat=STEEL) box_at(bm, (px, py - 0.15, 1.075), (0.40, 0.14, 0.016), r=0.004, mat=PAINT) box_at(bm, (px, py - 0.155, 1.085), (0.32, 0.09, 0.004), r=0.001, seg=1, mat=DARK) for k in range(10): for j in range(3): box_at(bm, (px - 0.135 + 0.03 * k, py - 0.125 - 0.03 * j, 1.0885), (0.024, 0.024, 0.003), r=0.001, seg=1, mat=DARK) ob = finish(bm, "SM_PDSliding_Monitor", C, mats, origin=P_MON) ob["closed_rotation"] = [0.0, 0.0, 0.0] return ob # ================================================================== CAMERA (moving) def build_camera(mats, C): bm = bmesh.new() M = Matrix.Translation(P_CAM) @ Matrix.Rotation(CAM_PITCH, 4, 'X') axis = lambda t: M @ Vector((0, -t, 0)) tube(bm, [axis(0.060), axis(0.20)], 0.028, 16, caps=('dome', 'flat'), mat=PAINT) # its back dome stops 1 cm short of the ball lathe(bm, [(0.016, -0.025), (0.016, -0.045), (0.0, -0.045)], 12, center=(0, 0, 0), axis='Y', mat=STEEL, M=M) # the neck from the ball lathe(bm, [(0.034, -0.16), (0.034, -0.215), (0.028, -0.215), (0.028, -0.16)], 16, center=(0, 0, 0), axis='Y', mat=DARK, M=M) # hood lathe(bm, [(0.019, -0.200), (0.019, -0.206), (0.0, -0.206)], 16, center=(0, 0, 0), axis='Y', mat=GLASS, M=M) # lens lathe(bm, [(0.004, -0.199), (0.004, -0.203), (0.0, -0.203)], 8, center=(0, 0, 0), axis='Y', mat=LRED, M=M @ Matrix.Translation((0.022, 0, 0.0))) ob = finish(bm, "SM_PDSliding_Camera", C, mats, origin=P_CAM) ob["closed_rotation"] = [0.0, 0.0, 0.0] return ob # ================================================================== assemble def build_all(): mats = get_mats() top = coll("PressureDoorSliding") cs, cm = coll("PDS | static", top), coll("PDS | moving", top) root = bpy.data.objects.get("PressureDoorSliding_Root") if root is None: root = bpy.data.objects.new("PressureDoorSliding_Root", None) root.empty_display_type = 'ARROWS' root.empty_display_size = 0.5 top.objects.link(root) obs = [build_frame(mats, cs), build_wall(mats, cs)] leafL, leafR = build_leaf(-1, mats, cm), build_leaf(1, mats, cm) movers = [leafL, leafR, build_monitor(mats, cm), build_camera(mats, cm)] for ob in obs + movers: ob.parent = root ob.matrix_parent_inverse = Matrix.Identity(4) handle = build_handle(mats, cm, leafL) root["screens"] = {k: {"c": list(v[0]), "u": list(v[1]), "v": list(v[2]), "w": v[3], "h": v[4], "region": v[5]} for k, v in SCREENS.items()} return dict(objects=[audit(o) for o in obs + movers + [handle]]) if not globals().get("SD_NO_AUTORUN"): RESULT = build_all()