"""Pressure door for the vessel's docking gate (the boarding gate in the works hall's port wall, where Anchorage's boarding bridge meets the ship). After the user's four reference renders of a round, hinged vault-style door (2026-10-14): a thick round leaf hung on a swing arm, a big hinge housing with a canister, an accumulator tank and pipes on the wall, bumper tubes round the frame, a floor plate with a recessed well and hazard stripes. Run in the open Blender (MCP): exec(open(r"D:\\HADAL - The Descent\\models\\pressure-door\\build\\pd_build.py").read()) Frame: metres, Z up, floor at z = 0. The wall's front face is y = 0; the door opens toward -Y (the "front", the side the references show). Seen from the front, +X is right; the hinge is on the right. Door axis (Y) at x = 0, z = ZC. How it moves (separate objects, origin on the axis; see pd_rig.py): SM_PressureDoor_Wheel hand wheel, turns -90 deg about its Y axis (unlocking) SM_PressureDoor_Bolt_* six bolts slide radially 0.28 m into the leaf (out of the frame's keepers) SM_PressureDoor_Leaf leaf + swing arm, turns +100 deg about the vertical hinge pin at (HX, HY) The pin sits in front of the leaf and outboard of it, so the leaf lifts straight out of its recess in the frame. Opening: 2.16 m clear (R 1.08), its bottom 0.20 m above the floor (a coaming with a tread at 0.25 m inside the frame; an Unreal character steps 0.45 m). The leaf clears the floor by 4 cm all through its swing. """ import bpy, bmesh, math, os from mathutils import Vector, Matrix BUILD = r"D:\HADAL - The Descent\models\pressure-door\build" exec(open(os.path.join(BUILD, "pd_lib.py"), encoding="utf-8").read()) # ------------------------------------------------------------------ dimensions ZC = 1.28 # door axis height R_OPEN = 1.08 # clear opening (the tunnel through the frame) R_LEAF = 1.24 # leaf R_REC = 1.28 # recess in the frame collar that the leaf sits in (4 cm round the rim) R_COL = 1.53 # collar outside R_BFL = 1.42 # back flange R_HOLE = 1.40 # hole in the wall plates (hidden under collar and flange) Y_SEAT, Y_LIP = -0.18, -0.24 # seat face (gasket), collar lip front Y_WF, Y_WB, Y_FB = 0.0, 0.30, 0.42 # wall front, wall back, back flange face HX, HY = 1.62, -0.62 # hinge pin (vertical), in front of the leaf and outboard of it N = 96 # segments round the door axis KEEP = [20, 70, 110, 160, 200, 340] # bolt / keeper angles, counter-clockwise from +X seen from the front BOLT_TIP, BOLT_LEN, BOLT_TRAVEL, BOLT_Y = 1.48, 0.42, 0.28, -0.31 SWING_DEG, WHEEL_DEG = 100.0, -90.0 WALL_X0, WALL_X1, WALL_Z1 = -2.30, 3.40, 3.40 FLOOR_Y0 = -3.80 MAT_NAMES = ["M_PD_Paint", "M_PD_Steel", "M_PD_Dark", "M_PD_Rubber", "M_PD_Hazard", "M_PD_Glass", "M_PD_Screen", "M_PD_LampRed", "M_PD_LampGreen", "M_PD_RedPaint"] PAINT, STEEL, DARK, RUBBER, HAZARD, GLASS, SCREEN, LRED, LGREEN, REDP = range(len(MAT_NAMES)) PREVIEW = {"M_PD_Paint": ((0.18, 0.36, 0.44), 0.0, 0.42), "M_PD_Steel": ((0.62, 0.63, 0.64), 1.0, 0.22), "M_PD_Dark": ((0.03, 0.033, 0.036), 0.3, 0.45), "M_PD_Rubber": ((0.018, 0.018, 0.019), 0.0, 0.8), "M_PD_Hazard": ((0.7, 0.1, 0.08), 0.0, 0.5), "M_PD_Glass": ((0.6, 0.7, 0.75), 0.0, 0.05), "M_PD_Screen": ((0.05, 0.4, 0.5), 0.0, 0.3), "M_PD_LampRed": ((0.9, 0.05, 0.03), 0.0, 0.3), "M_PD_LampGreen": ((0.05, 0.8, 0.2), 0.0, 0.3), "M_PD_RedPaint": ((0.55, 0.04, 0.03), 0.0, 0.4)} 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) out.append(m) return out 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 polar(r, phi_deg, y): a = math.radians(phi_deg) return Vector((r * math.cos(a), y, ZC + r * math.sin(a))) def frame_at(phi_deg, r=0.0, y=0.0, t=0.0): """matrix: local X radial at angle phi, local Y = world Y, local Z tangential (counter-clockwise)""" a = math.radians(phi_deg) return Matrix.Translation((0, 0, ZC)) @ mat_rot_y(a) @ Matrix.Translation((r, y, t)) def bolt_head(bm, M, r=0.02, mat=STEEL, n=10): """socket cap screw facing local -Y""" p = [(r, 0.003), (r, -0.010), (r * 0.75, -0.016), (r * 0.35, -0.016), (r * 0.35, -0.012), (0.0, -0.012)] lathe(bm, p, n, axis='Y', mat=mat, M=M) def rivet(bm, M, r=0.0065, mat=PAINT): lathe(bm, [(r, 0.001), (r, -0.0015), (r * 0.55, -0.0042), (0.0, -0.0048)], 6, axis='Y', mat=mat, M=M) 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("pd_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("pd_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 # ================================================================== FRAME (static) def build_frame(mats, C): bm = bmesh.new() spec = [(R_COL, Y_WF, 0), (R_COL, Y_LIP, 0.035), (R_REC, Y_LIP, 0.012), (R_REC, Y_SEAT, 0.008), (R_OPEN, Y_SEAT, 0.02), (R_OPEN, -0.02, 0), (R_OPEN + 0.014, -0.012, 0), (R_OPEN + 0.014, 0.012, 0), (R_OPEN, 0.02, 0), (R_OPEN, 0.20, 0), (R_OPEN + 0.014, 0.208, 0), (R_OPEN + 0.014, 0.232, 0), (R_OPEN, 0.24, 0), (R_OPEN, Y_FB, 0.02), (R_BFL, Y_FB, 0.025), (R_BFL, Y_WB, 0)] lathe(bm, prof(spec, 3), N, center=(0, 0, ZC), axis='Y', mat=PAINT) # gasket in the seat (1 cm proud; the leaf's back stops 2 mm short of it) lathe(bm, prof([(1.205, -0.172, 0), (1.205, -0.188, 0.004), (1.115, -0.188, 0.004), (1.115, -0.172, 0)], 2), N, center=(0, 0, ZC), axis='Y', mat=RUBBER) squash_to_floor(bm, 0.0, ZC) drop_floor_faces(bm) # keepers: a staple on the lip for each bolt (two legs, a bridge in front of the bolt) for phi in KEEP: for s in (-1, 1): rbox(bm, (0.19, 0.155, 0.035), 0.008, 2, frame_at(phi, 1.395, -0.3125, s * 0.0575), PAINT, drop=('+y',)) rbox(bm, (0.19, 0.04, 0.155), 0.012, 2, frame_at(phi, 1.395, -0.37, 0.0), PAINT) for s in (-1, 1): bolt_head(bm, frame_at(phi, 1.395, -0.39, s * 0.05) @ Matrix.Identity(4), 0.011, STEEL, 8) # lock box on top of the frame, with its window and handle (ref. picture 4) Mtop = frame_at(90.0, 1.41, -0.315, 0.0) rbox(bm, (0.22, 0.16, 0.34), 0.035, 3, Mtop, PAINT, drop=('+y',)) rbox(bm, (0.12, 0.012, 0.19), 0.004, 1, frame_at(90.0, 1.41, -0.396, 0.0), DARK) for (dr, dt, sr, st) in ((0.07, 0, 0.02, 0.23), (-0.07, 0, 0.02, 0.23), (0, 0.105, 0.16, 0.02), (0, -0.105, 0.16, 0.02)): rbox(bm, (sr, 0.016, st), 0.005, 1, frame_at(90.0, 1.41 + dr, -0.398, dt), STEEL) for k in range(5): # little lock pins behind the window rbox(bm, (0.035, 0.012, 0.022), 0.004, 1, frame_at(90.0, 1.41, -0.405, -0.06 + 0.03 * k), STEEL) hp = [frame_at(90.0, 1.47, -0.39, -0.14).to_translation(), frame_at(90.0, 1.47, -0.47, -0.14).to_translation(), frame_at(90.0, 1.47, -0.47, 0.14).to_translation(), frame_at(90.0, 1.47, -0.39, 0.14).to_translation()] tube(bm, fillet3(hp, 0.035, 4), 0.012, 10, caps=(None, None), mat=STEEL) # bumper tube round the upper left of the frame, on three clamps tube(bm, arc_pts((0, -0.10, ZC), 1.625, math.radians(96), math.radians(212), 44), 0.062, 14, mat=PAINT) for phi in (106, 140, 186): rbox(bm, (0.24, 0.18, 0.11), 0.05, 3, frame_at(phi, 1.60, -0.09, 0.0), PAINT, drop=('+y',)) rbox(bm, (0.05, 0.03, 0.125), 0.008, 1, frame_at(phi, 1.735, -0.10, 0.0), STEEL) # cap screws on the collar lip and on the back flange for k in range(48): phi = k * 7.5 + 3.75 if any(abs(((phi - a + 180) % 360) - 180) < 8.5 for a in KEEP) or abs(phi - 90) < 13: continue p = polar(1.48, phi, Y_LIP) if p.z < 0.09: continue bolt_head(bm, Matrix.Translation(p), 0.016) for k in range(40): p = polar(1.32, k * 9 + 4.5, Y_FB) if p.z < 0.09: continue bolt_head(bm, Matrix.Translation(p) @ Matrix.Rotation(math.pi, 4, 'Z'), 0.016) # tread in the bottom of the opening (top at 0.25 m), ribbed rbox(bm, (0.60, 0.60, 0.06), 0.006, 1, Matrix.Translation((0, 0.12, 0.22)), DARK) for k in range(7): rbox(bm, (0.026, 0.56, 0.012), 0.004, 1, Matrix.Translation((-0.24 + 0.08 * k, 0.12, 0.252)), DARK) # step on the back side rbox(bm, (1.00, 0.36, 0.13), 0.012, 2, Matrix.Translation((0, Y_FB + 0.18, 0.065)), PAINT, drop=('-z',)) return finish(bm, "SM_PressureDoor_Frame", C, mats) # ================================================================== LEAF + ARM (moving: turns about the hinge pin) def build_leaf(mats, C): bm = bmesh.new() spec = [(0.0, -0.19, 0), (1.19, -0.19, 0.03), (R_LEAF, -0.235, 0.03), (R_LEAF, -0.43, 0.08), (1.12, -0.55, 0.06), (1.04, -0.55, 0.03), (1.00, -0.47, 0.02), (0.76, -0.47, 0.02), (0.72, -0.53, 0.03), (0.60, -0.53, 0.02), (0.56, -0.48, 0.02), (0.0, -0.48, 0)] lathe(bm, prof(spec, 3), N, center=(0, 0, ZC), axis='Y', mat=PAINT) # back: a stiffener ring and the manual-release boss lathe(bm, prof([(0.56, -0.195, 0), (0.56, -0.15, 0.01), (0.66, -0.15, 0.01), (0.66, -0.195, 0)], 2), 64, center=(0, 0, ZC), axis='Y', mat=PAINT) lathe(bm, prof([(0.0, -0.13, 0), (0.16, -0.13, 0.02), (0.20, -0.17, 0.01), (0.20, -0.195, 0)], 2), 48, center=(0, 0, ZC), axis='Y', mat=PAINT) # back face (seen when the door stands open): stiffener bars out to each bolt, a pull rail, screws on the ring. # Everything stays inside the opening's radius and stands at most 9 cm proud, so it leaves the tunnel cleanly. for phi in KEEP: rbox(bm, (0.70, 0.065, 0.07), 0.02, 2, frame_at(phi, 0.58, -0.1625, 0.0), PAINT) bolt_head(bm, frame_at(phi, 0.90, -0.13, 0.0) @ Matrix.Rotation(math.pi, 4, 'Z'), 0.014) rail = [polar(0.80, 242, -0.195), polar(0.80, 242, -0.10)] rail += [polar(0.80, 242 + 56 * k / 12, -0.10) for k in range(1, 12)] rail += [polar(0.80, 298, -0.10), polar(0.80, 298, -0.195)] tube(bm, fillet3(rail, 0.03, 4), 0.016, 10, caps=(None, None), mat=STEEL) for k in range(16): phi = k * 22.5 + 11.25 bolt_head(bm, Matrix.Translation(polar(0.61, phi, -0.15)) @ Matrix.Rotation(math.pi, 4, 'Z'), 0.013) # hub boss that carries the arm and the wheel shaft lathe(bm, prof([(0.245, -0.46, 0), (0.245, -0.66, 0.03), (0.20, -0.70, 0.012), (0.0, -0.70, 0)], 3), 64, center=(0, 0, ZC), axis='Y', mat=PAINT) # ribs across the recessed ring (none at 0 deg: the arm is there) for phi in (45, 90, 135, 180, 225, 270, 315): rbox(bm, (0.30, 0.07, 0.05), 0.015, 2, frame_at(phi, 0.88, -0.49, 0.0), PAINT) # lock housings on the rim, one per bolt; bezels where the bolts leave the rim for phi in KEEP: rbox(bm, (0.20, 0.17, 0.15), 0.035, 3, frame_at(phi, 1.10, -0.545, 0.0), PAINT) rbox(bm, (0.09, 0.012, 0.10), 0.006, 1, frame_at(phi, 1.10, -0.632, 0.0), STEEL) rbox(bm, (0.012, 0.006, 0.06), 0.002, 1, frame_at(phi, 1.10, -0.639, 0.0), DARK) for (dy, dt, sy, st) in ((0.04875, 0, 0.0225, 0.13), (-0.04875, 0, 0.0225, 0.13), (0, 0.05375, 0.075, 0.0225), (0, -0.05375, 0.075, 0.0225)): rbox(bm, (0.019, sy, st), 0.004, 1, frame_at(phi, 1.2425, BOLT_Y + dy, dt), STEEL) # swing arm from the hub to the hinge knuckle, with a grab handle rbox(bm, (1.46, 0.16, 0.30), 0.045, 3, Matrix.Translation((0.83, HY, ZC)), PAINT) rbox(bm, (1.10, 0.02, 0.12), 0.008, 1, Matrix.Translation((0.80, HY - 0.085, ZC)), PAINT) # knuckle with a bore for the pin (r 0.05 round the 0.045 pin) lathe(bm, prof([(0.05, ZC + 0.18, 0), (0.10, ZC + 0.18, 0.025), (0.12, ZC + 0.16, 0.0), (0.12, ZC - 0.16, 0.025), (0.10, ZC - 0.18, 0), (0.05, ZC - 0.18, 0), (0.05, ZC + 0.18, 0)], 2), 40, center=(HX, HY, 0), axis='Z', mat=PAINT) hp = [(1.04, HY - 0.08, ZC + 0.10), (1.04, HY - 0.21, ZC + 0.10), (1.04, HY - 0.21, ZC - 0.10), (1.04, HY - 0.08, ZC - 0.10)] tube(bm, fillet3(hp, 0.04, 5), 0.018, 12, caps=(None, None), mat=STEEL) for z in (ZC + 0.10, ZC - 0.10): lathe(bm, [(0.03, HY - 0.07), (0.03, HY - 0.095), (0.022, HY - 0.105), (0.0, HY - 0.105)], 12, center=(1.04, 0, z), axis='Y', mat=STEEL) for x in (0.40, 1.42): bolt_head(bm, Matrix.Translation((x, HY - 0.08, ZC + 0.10)), 0.014) bolt_head(bm, Matrix.Translation((x, HY - 0.08, ZC - 0.10)), 0.014) # bumper tube along the bottom of the rim, and a chrome grab rail tube(bm, arc_pts((0, -0.592, ZC), 1.11, math.radians(213), math.radians(327), 36), 0.045, 12, mat=PAINT) gr = [polar(0.92, 232, -0.47)] + [polar(0.92, 232, -0.60)] gr += [polar(0.92, 232 + 76 * k / 16, -0.60) for k in range(1, 16)] gr += [polar(0.92, 308, -0.60), polar(0.92, 308, -0.47)] tube(bm, fillet3(gr, 0.035, 4), 0.016, 10, caps=(None, None), mat=STEEL) # cap screws round the rim front for k in range(48): phi = k * 7.5 + 3.75 if any(abs(((phi - r + 180) % 360) - 180) < 4.5 for r in (45, 90, 135, 180, 225, 270, 315)) or phi < 11 or phi > 349: continue bolt_head(bm, Matrix.Translation(polar(0.965, phi, -0.47)), 0.013) return finish(bm, "SM_PressureDoor_Leaf", C, mats, origin=(HX, HY, ZC)) def build_wheel(mats, C): bm = bmesh.new() y = -0.765 lathe(bm, prof([(0.075, -0.69, 0), (0.075, -0.79, 0.015), (0.05, -0.815, 0.01), (0.0, -0.815, 0)], 2), 24, center=(0, 0, ZC), axis='Y', mat=STEEL) tube(bm, arc_pts((0, y, ZC), 0.25, 0.0, TAU, 48)[:-1], 0.026, 12, closed=True, mat=STEEL) for k in range(4): a = math.radians(45 + 90 * k) p0 = Vector((0.06 * math.cos(a), y, ZC + 0.06 * math.sin(a))) p1 = Vector((0.24 * math.cos(a), y, ZC + 0.24 * math.sin(a))) tube(bm, [p0, p1], 0.017, 10, caps=(None, None), mat=STEEL) # a spinner knob on the rim lathe(bm, prof([(0.022, y - 0.02, 0), (0.022, y - 0.10, 0.01), (0.0, y - 0.10, 0)], 2), 12, center=(0.25, 0, ZC), axis='Y', mat=DARK) return finish(bm, "SM_PressureDoor_Wheel", C, mats, origin=(0, y, ZC)) def build_bolt_mesh(mats): bm = bmesh.new() rbox(bm, (BOLT_LEN, 0.06, 0.07), 0.014, 2, Matrix.Translation((-BOLT_LEN / 2, 0, 0)), STEEL) me = bpy.data.meshes.get("SM_PressureDoor_Bolt") or bpy.data.meshes.new("SM_PressureDoor_Bolt") me.clear_geometry() me.materials.clear() bm.to_mesh(me) bm.free() for p in me.polygons: p.material_index = 0 me.materials.append(mats[STEEL]) me.shade_smooth() me.set_sharp_from_angle(angle=math.radians(38)) return me # ================================================================== HOUSING (static): hinge, canister, tank, pipes, panel def build_housing(mats, C): bm = bmesh.new() out = fillet2([(1.56, 0.0), (2.72, 0.0), (2.72, 1.90), (2.34, 2.28), (1.56, 2.28)], [0, 0, 0.06, 0.06, 0.06], 3, closed=True) slab(bm, out, 0.44, 0.07, 4, origin=(0, 0, 0), mat=PAINT) pan = fillet2([(1.92, 0.30), (2.60, 0.30), (2.60, 1.82), (2.30, 2.12), (1.92, 2.12)], 0.04, 3, closed=True) slab(bm, pan, 0.035, 0.018, 2, origin=(0, -0.435, 0), mat=PAINT) # hinge: two lugs, the pin and its caps (the leaf's knuckle turns between the lugs) for s in (1, -1): rbox(bm, (0.28, 0.36, 0.14), 0.04, 3, Matrix.Translation((HX, -0.58, ZC + s * 0.26)), PAINT) lathe(bm, prof([(0.0, 0.03 * s, 0), (0.07, 0.03 * s, 0.01), (0.075, 0.0, 0), (0.075, -0.01 * s, 0)], 2) if s > 0 else prof([(0.075, 0.01, 0), (0.075, 0.0, 0.0), (0.07, -0.03, 0.01), (0.0, -0.03, 0)], 2), 24, center=(HX, HY, ZC + s * 0.33), axis='Z', mat=STEEL) lathe(bm, [(0.045, ZC + 0.335), (0.045, ZC - 0.335)], 16, center=(HX, HY, 0), axis='Z', mat=STEEL) # canister with a window (ref. picture 2), half sunk into the panel cx, cy = 2.42, -0.55 lathe(bm, prof([(0.0, 1.06, 0), (0.15, 1.06, 0.02), (0.185, 1.02, 0.02), (0.185, 0.91, 0.01), (0.165, 0.90, 0)], 3), 32, center=(cx, cy, 0), axis='Z', mat=PAINT) lathe(bm, prof([(0.165, 0.53, 0), (0.185, 0.52, 0.01), (0.185, 0.40, 0.02), (0.15, 0.36, 0.02), (0.0, 0.36, 0)], 3), 32, center=(cx, cy, 0), axis='Z', mat=PAINT) lathe(bm, [(0.165, 0.905), (0.165, 0.525)], 32, center=(cx, cy, 0), axis='Z', mat=GLASS) lathe(bm, prof([(0.0, 0.91, 0), (0.11, 0.91, 0.01), (0.11, 0.52, 0.01), (0.0, 0.52, 0)], 2), 24, center=(cx, cy, 0), axis='Z', mat=STEEL) for k in range(5): a = math.radians(-90 + (k - 2) * 32) x, y = cx + 0.178 * math.cos(a), cy + 0.178 * math.sin(a) tube(bm, [(x, y, 0.51), (x, y, 0.92)], 0.011, 8, caps=(None, None), mat=STEEL) for z in (0.66, 0.78): lathe(bm, [(0.184, z + 0.012), (0.19, z + 0.006), (0.19, z - 0.006), (0.184, z - 0.012)], 32, center=(cx, cy, 0), axis='Z', mat=STEEL) # bumper bar low on the housing (the 'cigar' in ref. picture 2) tube(bm, [(1.74, -0.53, 0.24), (2.62, -0.53, 0.24)], 0.07, 16, mat=PAINT) for x in (1.86, 2.50): rbox(bm, (0.10, 0.12, 0.10), 0.03, 2, Matrix.Translation((x, -0.47, 0.24)), PAINT, drop=('+y',)) # control panel: screen, keypad, status lamps rbox(bm, (0.36, 0.05, 0.42), 0.02, 2, Matrix.Translation((2.06, -0.485, 1.34)), PAINT, drop=('+y',)) rbox(bm, (0.29, 0.012, 0.17), 0.006, 1, Matrix.Translation((2.06, -0.512, 1.44)), DARK) rbox(bm, (0.26, 0.004, 0.14), 0.002, 1, Matrix.Translation((2.06, -0.5185, 1.44)), SCREEN) for i, x in enumerate((1.98, 2.06, 2.14)): for z in (1.28, 1.22): rbox(bm, (0.06, 0.02, 0.045), 0.008, 2, Matrix.Translation((x, -0.515, z)), DARK, drop=('+y',)) for x, m in ((1.99, LRED), (2.13, LGREEN)): lathe(bm, prof([(0.022, -0.505, 0), (0.022, -0.515, 0.005), (0.014, -0.525, 0.004), (0.0, -0.527, 0)], 2), 16, center=(x, 0, 1.162), axis='Y', mat=m) # number, embossed tm = text_mesh("01", 0.46, 0.007) add_mesh(bm, tm, Matrix.Translation((2.44, -0.4745, 1.64)) @ Matrix.Rotation(math.radians(90), 4, 'X'), PAINT) bpy.data.meshes.remove(tm) # vents on the housing's low panel for k in range(4): rbox(bm, (0.26, 0.012, 0.022), 0.006, 1, Matrix.Translation((2.08, -0.473, 1.99 - 0.045 * k)), DARK) # accumulator tank over the housing, on two wall brackets with straps tp = prof([(0.0, 2.88, 0), (0.17, 2.86, 0.05), (0.24, 2.76, 0.06), (0.24, 1.90, 0.06), (0.17, 1.80, 0.05), (0.0, 1.78, 0)], 4) lathe(bm, tp, 40, center=(0, -0.30, 2.62), axis='X', mat=PAINT) for x in (2.05, 2.60): lathe(bm, [(0.243, x + 0.03), (0.252, x + 0.02), (0.252, x - 0.02), (0.243, x - 0.03)], 40, center=(0, -0.30, 2.62), axis='X', mat=STEEL) rbox(bm, (0.08, 0.30, 0.12), 0.02, 2, Matrix.Translation((x, -0.15, 2.36)), PAINT, drop=('+y',)) # pipe from the tank's end down into the duct, with a coupling tube(bm, fillet3([(2.80, -0.30, 2.62), (3.06, -0.30, 2.62), (3.06, -0.30, 1.56)], 0.14, 6), 0.05, 16, caps=(None, None), mat=PAINT) lathe(bm, prof([(0.05, 2.02, 0), (0.064, 2.01, 0.004), (0.064, 1.96, 0), (0.058, 1.955, 0), (0.058, 1.925, 0), (0.064, 1.92, 0.004), (0.064, 1.87, 0.004), (0.05, 1.86, 0)], 1), 20, center=(3.06, -0.30, 0), axis='Z', mat=STEEL) rbox(bm, (0.32, 0.42, 1.62), 0.04, 3, Matrix.Translation((3.06, -0.21, 0.81)), PAINT, drop=('+y', '-z')) rbox(bm, (0.20, 0.012, 0.30), 0.008, 1, Matrix.Translation((3.06, -0.425, 0.55)), DARK) # conduits from the tank along the top of the wall for i, (x, z) in enumerate(((2.12, 3.00), (2.24, 3.07), (2.36, 3.14))): tube(bm, fillet3([(x, -0.30, 2.80), (x, -0.30, z - 0.12), (x, -0.06, z), (WALL_X1, -0.06, z)], 0.06, 5), 0.016, 10, caps=(None, 'flat'), mat=STEEL if i == 1 else PAINT) for x in (2.75, 3.20): rbox(bm, (0.045, 0.085, 0.22), 0.012, 2, Matrix.Translation((x, -0.0425, 3.07)), DARK, drop=('+y',)) # back side: a small panel beside the opening rbox(bm, (0.30, 0.04, 0.40), 0.015, 2, Matrix.Translation((1.85, Y_WB + 0.02, 1.34)), PAINT, drop=('-y',)) rbox(bm, (0.22, 0.008, 0.13), 0.004, 1, Matrix.Translation((1.85, Y_WB + 0.042, 1.43)), SCREEN) for x, m in ((1.79, LRED), (1.91, LGREEN)): lathe(bm, prof([(0.0, Y_WB + 0.05, 0), (0.018, Y_WB + 0.046, 0.004), (0.018, Y_WB + 0.038, 0)], 2), 12, center=(x, 0, 1.25), axis='Y', mat=m, M=None) drop_floor_faces(bm) return finish(bm, "SM_PressureDoor_Housing", C, mats) # ================================================================== BULKHEAD (static wall plates round the door) def _ray_rect(phi, x0, x1, z0, z1): dx, dz = math.cos(phi), math.sin(phi) ts = [] if dx > 1e-9: ts.append(x1 / dx) if dx < -1e-9: ts.append(x0 / dx) if dz > 1e-9: ts.append((z1 - ZC) / dz) if dz < -1e-9: ts.append((z0 - ZC) / dz) t = min(ts) return Vector((t * dx, ZC + t * dz)) def hole_panel(bm, x0, x1, z0, z1, y, facing, ch=0.006): """a plate with the round hole, quads from the hole to the rectangle along rays from the door axis""" corners = [math.atan2(z - ZC, x) for x, z in ((x0, z0), (x1, z0), (x1, z1), (x0, z1))] angs = sorted(set([TAU * k / N - math.pi for k in range(N)] + corners)) hole, inset, outer = [], [], [] for a in angs: h = Vector((R_HOLE * math.cos(a), ZC + R_HOLE * math.sin(a))) if h.y < 0: h = _ray_rect(a, x0, x1, 0.0, z1) hole.append(h) inset.append(_ray_rect(a, x0 + ch, x1 - ch, z0, z1 - ch)) outer.append(_ray_rect(a, x0, x1, z0, z1)) ys = y - facing * 0.0 yb = y + facing * ch # the chamfer runs back into the wall def vv(p, yy): return bm.verts.new((p.x, yy, p.y)) H = [vv(p, ys) for p in hole] I = [vv(p, ys) for p in inset] O = [vv(p, yb) for p in outer] n = len(angs) out = Vector((0, -facing, 0)) for k in range(n): k2 = (k + 1) % n face(bm, [H[k], H[k2], I[k2], I[k]], PAINT, out=out) face(bm, [I[k], I[k2], O[k2], O[k]], PAINT, out=out) def build_bulkhead(mats, C): bm = bmesh.new() fr = (V(1, 0, 0), V(0, 0, 1), V(0, -1, 0)) hole_panel(bm, -1.66, 1.66, 0.0, 2.96, Y_WF, 1.0) for (x0, x1, z0, z1) in ((WALL_X0, -1.66, 0.0, 1.70), (WALL_X0, -1.66, 1.70, WALL_Z1), (-1.66, 1.66, 2.96, WALL_Z1), (1.66, WALL_X1, 0.0, 2.40), (1.66, WALL_X1, 2.40, WALL_Z1)): slab(bm, [Vector((x0, z0)), Vector((x1, z0)), Vector((x1, z1)), Vector((x0, z1))], 0.006, 0.006, 2, origin=(0, 0.006, 0), basis=fr, mat=PAINT) # back face (one plate) and the wall's ends hole_panel(bm, WALL_X0, WALL_X1, 0.0, WALL_Z1, Y_WB, -1.0) face(bm, [bm.verts.new(p) for p in ((WALL_X0, 0.006, 0), (WALL_X0, Y_WB - 0.006, 0), (WALL_X0, Y_WB - 0.006, WALL_Z1 - 0.006), (WALL_X0, 0.006, WALL_Z1 - 0.006))], PAINT, out=V(-1, 0, 0)) face(bm, [bm.verts.new(p) for p in ((WALL_X1, 0.006, 0), (WALL_X1, Y_WB - 0.006, 0), (WALL_X1, Y_WB - 0.006, WALL_Z1 - 0.006), (WALL_X1, 0.006, WALL_Z1 - 0.006))], PAINT, out=V(1, 0, 0)) face(bm, [bm.verts.new(p) for p in ((WALL_X0 + 0.006, 0.006, WALL_Z1), (WALL_X1 - 0.006, 0.006, WALL_Z1), (WALL_X1 - 0.006, Y_WB - 0.006, WALL_Z1), (WALL_X0 + 0.006, Y_WB - 0.006, WALL_Z1))], PAINT, out=V(0, 0, 1)) # rivet rows beside the seams, not where something covers the wall def covered(x, z): if math.hypot(x, z - ZC) < 1.62: return True if 1.50 < x < 2.78 and z < 2.34: return True # housing if 1.76 < x < 2.92 and 2.30 < z < 2.92: return True # tank and its brackets if 2.86 < x < 3.26 and z < 1.66: return True # duct if x > 2.0 and 2.95 < z < 3.20: return True # conduits return False seams = [((-1.66, 0.04), (-1.66, WALL_Z1 - 0.04), 'v'), ((1.66, 0.04), (1.66, WALL_Z1 - 0.04), 'v'), ((-1.62, 2.96), (1.62, 2.96), 'h'), ((WALL_X0 + 0.05, 1.70), (-1.70, 1.70), 'h'), ((1.70, 2.40), (WALL_X1 - 0.05, 2.40), 'h')] for (a, b, kind) in seams: a, b = Vector(a), Vector(b) L = (b - a).length cnt = int(L / 0.075) for i in range(cnt + 1): p = a + (b - a) * (i / max(cnt, 1)) for s in (-1, 1): q = p + (Vector((s * 0.035, 0)) if kind == 'v' else Vector((0, s * 0.035))) if not covered(q.x, q.y): rivet(bm, Matrix.Translation((q.x, 0.0, q.y))) drop_floor_faces(bm) return finish(bm, "SM_PressureDoor_Bulkhead", C, mats) # ================================================================== FLOOR PLATE (static): tread plates, well, markings def plate_with_hole(bm, o, i, t=0.03, ch=0.004): """rectangle o=(x0,x1,y0,y1) with the rectangular hole i, top at z = 0""" (ox0, ox1, oy0, oy1), (ix0, ix1, iy0, iy1) = o, i B = [bm.verts.new(p) for p in ((ox0, oy0, -t), (ox1, oy0, -t), (ox1, oy1, -t), (ox0, oy1, -t))] S = [bm.verts.new(p) for p in ((ox0, oy0, -ch), (ox1, oy0, -ch), (ox1, oy1, -ch), (ox0, oy1, -ch))] T = [bm.verts.new(p) for p in ((ox0 + ch, oy0 + ch, 0), (ox1 - ch, oy0 + ch, 0), (ox1 - ch, oy1 - ch, 0), (ox0 + ch, oy1 - ch, 0))] H = [bm.verts.new(p) for p in ((ix0, iy0, 0), (ix1, iy0, 0), (ix1, iy1, 0), (ix0, iy1, 0))] c = Vector(((ox0 + ox1) / 2, (oy0 + oy1) / 2, -t / 2)) for k in range(4): k2 = (k + 1) % 4 face(bm, [B[k], B[k2], S[k2], S[k]], PAINT, ref=c) face(bm, [S[k], S[k2], T[k2], T[k]], PAINT, ref=c) face(bm, [T[k], T[k2], H[k2], H[k]], PAINT, out=V(0, 0, 1)) def build_floor(mats, C): bm = bmesh.new() fl = (V(1, 0, 0), V(0, 1, 0), V(0, 0, 1)) xs = [WALL_X0, -1.10, 0.10, 1.30, 2.50, WALL_X1] ys = [FLOOR_Y0, -2.60, -1.40, -0.65, 0.0] well = (-2.15, -1.45, -1.15, -0.75) for i in range(len(xs) - 1): for j in range(len(ys) - 1): x0, x1, y0, y1 = xs[i], xs[i + 1], ys[j], ys[j + 1] if x0 <= well[0] and x1 >= well[1] and y0 <= well[2] and y1 >= well[3]: plate_with_hole(bm, (x0, x1, y0, y1), well) else: slab(bm, [Vector((x0, y0)), Vector((x1, y0)), Vector((x1, y1)), Vector((x0, y1))], 0.03, 0.004, 1, origin=(0, 0, -0.03), basis=fl, mat=PAINT) # anti-slip strips on each plate (a few raised lines like the reference) for k in range(3): yy = y0 + 0.18 + 0.05 * k if not (x0 <= well[1] + 0.05 and x1 >= well[0] - 0.05 and well[2] - 0.05 <= yy <= well[3] + 0.05): rbox(bm, (0.36, 0.014, 0.006), 0.003, 1, Matrix.Translation((x0 + 0.30, yy, 0.0)), PAINT, drop=('-z',)) # the well: an open box let into the floor with a hydraulic accumulator in it (ref. pictures 2 and 3) wx, wy = (well[0] + well[1]) / 2, (well[2] + well[3]) / 2 rbox(bm, (0.70, 0.40, 0.16), 0.012, 2, Matrix.Translation((wx, wy, -0.08)), PAINT, drop=('+z',), flip=True) for (dx, dy, sx, sy) in ((0, 0.215, 0.76, 0.03), (0, -0.215, 0.76, 0.03), (0.365, 0, 0.03, 0.40), (-0.365, 0, 0.03, 0.40)): rbox(bm, (sx, sy, 0.012), 0.004, 1, Matrix.Translation((wx + dx, wy + dy, 0.002)), STEEL, drop=('-z',)) for dy in (-0.085, 0.085): tube(bm, [(wx - 0.26, wy + dy, -0.075), (wx + 0.26, wy + dy, -0.075)], 0.05, 16, caps=('dome', 'dome'), mat=STEEL) for dx in (-0.12, 0.10): lathe(bm, [(0.051, wx + dx + 0.015), (0.053, wx + dx + 0.01), (0.053, wx + dx - 0.01), (0.051, wx + dx - 0.015)], 16, center=(0, wy + dy, -0.075), axis='X', mat=REDP) tube(bm, fillet3([(wx - 0.20, wy, -0.13), (wx - 0.20, wy, -0.02), (wx + 0.20, wy, -0.02), (wx + 0.20, wy, -0.13)], 0.03, 4), 0.012, 8, caps=(None, None), mat=STEEL) # markings: a hazard band before the threshold, and the arc the leaf's far edge sweeps def strip(pts_l, pts_r, u_len): L = [bm.verts.new(p) for p in pts_l] R = [bm.verts.new(p) for p in pts_r] fs = [] for k in range(len(L) - 1): fs.append(face(bm, [L[k], R[k], R[k + 1], L[k + 1]], HAZARD, out=V(0, 0, 1))) return fs zdec = 0.0015 strip([(x, -0.60, zdec) for x in (-1.05, 1.05)], [(x, -0.72, zdec) for x in (-1.05, 1.05)], 0.5) ra, a0, a1 = 2.95, math.radians(176), math.radians(282) ns = 40 strip([(HX + (ra - 0.03) * math.cos(a0 + (a1 - a0) * k / ns), HY + (ra - 0.03) * math.sin(a0 + (a1 - a0) * k / ns), zdec) for k in range(ns + 1)], [(HX + (ra + 0.03) * math.cos(a0 + (a1 - a0) * k / ns), HY + (ra + 0.03) * math.sin(a0 + (a1 - a0) * k / ns), zdec) for k in range(ns + 1)], 0.5) return finish(bm, "SM_PressureDoor_FloorPlate", C, mats) # ================================================================== assemble def build_all(): mats = get_mats() top = coll("PressureDoor") cs, cm = coll("PD | static", top), coll("PD | moving", top) root = bpy.data.objects.get("PressureDoor_Root") if root is None: root = bpy.data.objects.new("PressureDoor_Root", None) root.empty_display_type = 'ARROWS' root.empty_display_size = 0.5 top.objects.link(root) obs = [build_frame(mats, cs), build_housing(mats, cs), build_bulkhead(mats, cs), build_floor(mats, cs)] leaf = build_leaf(mats, cm) wheel = build_wheel(mats, cm) bolt_me = build_bolt_mesh(mats) for ob in obs + [leaf]: ob.parent = root ob.matrix_parent_inverse = Matrix.Identity(4) P = Vector((HX, HY, ZC)) wheel.parent = leaf wheel.matrix_parent_inverse = Matrix.Identity(4) wheel.location = Vector((0, -0.765, ZC)) - P bolts = [] for i, phi in enumerate(KEEP): nm = "SM_PressureDoor_Bolt_%02d" % (i + 1) ob = bpy.data.objects.get(nm) or bpy.data.objects.new(nm, bolt_me) ob.data = bolt_me if nm not in cm.objects: cm.objects.link(ob) ob.parent = leaf ob.matrix_parent_inverse = Matrix.Identity(4) ob.rotation_euler = (0, -math.radians(phi), 0) ob.location = polar(BOLT_TIP, phi, BOLT_Y) - P ob["closed_location"] = list(ob.location) ob["phi_deg"] = phi bolts.append(ob) leaf["closed_rotation_z"] = 0.0 return dict(objects=[audit(o) for o in obs + [leaf, wheel]], bolt=audit(bolts[0])) if not globals().get("PD_NO_AUTORUN"): RESULT = build_all()