# Grouper: the cabin (exec'd by build_grouper.py after gr_doors.py) after SECTION A-A (top right; the bottom-left one for the # overhead panels), the concept's look: a dark cabin with cyan screens, two pilots side by side (no figures). # * deck top z -1.198 (the section's floor slab) from the rear bulkhead (x 715, aft of the doors and the hatch) to where the # lining's bottom rises into the cheek (x 848); the lining (gr_hull.py) is the cabin's wall, 113 mm inside the skin; # * two seats side by side at X +-0.45 (moved in from the drawn +-0.61 so each eye looks straight out through the front # window: gr_measure CABIN), backs at x 800, cushion top z -0.692, back top 0.275, eyes (+-0.45, -1.146, 0.088); # * the console column between them (0.34 m wide) up to z 0.574 with its control box (z -0.575 .. -0.185); a low dashboard # under the front window with a screen for each seat; overhead screens (the section's three); wall control panels at # z -0.81 .. -0.23 outboard of the seats; a ladder under the hatch. # Everything is checked inside the lining (gr_finish.py: no cabin vertex within 5 mm of the skin or the glass). Z_DECK = CABIN["deck_top_z"] SEAT_X = CABIN["seat_lateral"] SEAT_W = CABIN["seat_width"] Y_SEAT_BACK = Yi(CABIN_Y["seat_back_x"]) Y_EYE = Yi(CABIN_Y["eye_x"]) Z_CUSH = CABIN["cushion_top_z"] Z_BACK_TOP = CABIN["seat_back_top_z"] EYE_Z = CABIN["eye_z"] def inner_x(z, y, clear=0.012): """the lining's lateral offset at height z: the hull's main body WITHOUT the pods (the lining follows it, gr_hull.py) at that height, less the lining (measured along the section's normal) and a clearance""" P_, arc_ = prof_at(y, False) best = None for i in range(1, len(P_.p)): (l0, z0), (l1, z1) = P_.p[i - 1], P_.p[i] if (z0 - z) * (z1 - z) <= 0 and z0 != z1: t = (z - z0) / (z1 - z0) lat = l0 + (l1 - l0) * t nz = abs(l1 - l0) / max(1e-9, math.hypot(l1 - l0, z1 - z0)) # the flank's slope: the lining's lateral depth if best is None or lat > best[0]: best = (lat, (LINER + clear) / max(0.3, math.sqrt(max(0.0, 1 - nz * nz)))) if best is None: return 0.05 return best[0] - best[1] def inner_top(x, y, clear=0.012): return hull_z_at(x, y, top=True) - LINER - clear def inner_bot(x, y, clear=0.012): return hull_z_at(x, y, top=False) + LINER + clear # ---- the deck: a slab top from the rear bulkhead to x 848, as wide as the lining at its height Y_DECK0, Y_DECK1 = Yi(716.0), Yi(843.0) rowsd = [] nd = 22 for q in range(nd + 1): y = Y_DECK0 + (Y_DECK1 - Y_DECK0) * math.sin(0.5 * math.pi * q / nd) # rows closer toward the front (the pods' noses) w = min(inner_x(Z_DECK, y, 0.02), inner_x(Z_DECK + 0.02, y, 0.02)) rowsd.append([Vector((x_, y, Z_DECK)) for x_ in [-w + 2 * w * c / 8 for c in range(9)]]) grid_rings(cab, rowsd, CABF, closed_u=False, expect=(nd // 2, 4, (0, 0, 1))) # ---- two BUCKET seats side by side (critique round 1 M7: SECTION A-A draws contoured bucket seats with headrests, side # bolsters, cushions and outboard armrest pads with controls; they were a slab and a box). Lofted quads: the pan and the back # are sections with raised side bolsters (ladder caps: no folds on the non-convex section), the headrest a winged pad on two # posts, the outboard armrest a pad with a control panel and a side stick. Seats at X +-0.45, eyes unchanged (view check). SEAT_EYES = {} REC = math.radians(10.0) # the backs recline 10 deg aft PAN_SEC = [(-0.28, -0.045), (-0.28, 0.045), (-0.262, 0.085), (-0.228, 0.088), (-0.196, 0.050), (-0.150, 0.030), (-0.050, 0.024)] PAN_SEC = PAN_SEC + [(-x, z) for x, z in reversed(PAN_SEC)] BACK_SEC = [(-0.250, 0.052), (-0.280, 0.012), (-0.28, -0.080), (-0.258, -0.122), (-0.222, -0.118), (-0.192, -0.062), (-0.140, -0.036), (-0.050, -0.028)] # the rear corners rounded (a shell), the bolsters forward BACK_SEC = BACK_SEC + [(-x, d) for x, d in reversed(BACK_SEC)] def bolster_scale(sec, k, face): """the section with its bolsters (the points beyond |x| 0.17) eased toward the seat's face level `face` by k (1 = full)""" out = [] for x, z in sec: if abs(x) > 0.17 and (z - face) * (sec[3][1] - face) > 0: z = face + (z - face) * k out.append((x, z)) return out for sg in (1, -1): cx = sg * SEAT_X yc = Y_SEAT_BACK - 0.30 # pedestal and its foot, the bucket's shell under the pan qcyl(cab, 0.075, Z_CUSH - 0.10 - Z_DECK, Vector((cx, yc + 0.04, 0.5 * (Z_CUSH - 0.10 + Z_DECK))), CABD, segs=16) qbox(cab, (0.36, 0.40, 0.035), Vector((cx, yc + 0.04, Z_DECK + 0.018)), CABD, bevel=0.008) qbox(cab, (0.40, 0.48, 0.05), Vector((cx, yc - 0.01, Z_CUSH - 0.085)), CABD, bevel=0.012) # the pan: front (waterfall) to back rings = [] for dy, dz, k in ((-0.275, -0.030, 0.80), (-0.255, -0.006, 0.90), (-0.20, 0.0, 1.0), (0.0, 0.0, 1.0), (0.20, -0.004, 1.0), (0.255, -0.010, 0.9)): rings.append([Vector((cx + x, yc + dy, Z_CUSH - 0.04 + dz + z)) for x, z in bolster_scale(PAN_SEC, k, 0.03)]) loft_rings(cab, rings, SEAT, ladder=True) # the back: from the pan's rear edge up along the reclined direction, the bolsters easing toward the shoulders up = Vector((0, math.sin(REC), math.cos(REC))) fwd = Vector((0, -math.cos(REC), math.sin(REC))) # out of the back's face (forward, tilted up with the recline) base = Vector((cx, Y_SEAT_BACK + 0.02, Z_CUSH - 0.06)) hb = Z_BACK_TOP - (Z_CUSH - 0.06) rings = [] # tapered with shoulder wings and a rounded top (from behind, as SECTION A-A sees them, a bucket seat's outline) for t, k, w in ((0.0, 0.9, 0.90), (0.12, 1.0, 0.94), (0.45, 1.0, 1.0), (0.70, 0.95, 1.03), (0.86, 0.75, 0.92), (0.96, 0.62, 0.78), (1.0, 0.55, 0.64)): c = base + up * (hb * t) rings.append([c + Vector((x * w, 0, 0)) - fwd * d for x, d in bolster_scale(BACK_SEC, k, -0.03)]) loft_rings(cab, rings, SEAT, ladder=True) # the headrest: a winged pad on two posts over the back's top top = base + up * hb hcen = top + up * 0.15 for sx in (-0.07, 0.07): tube(cab, [top + Vector((sx, 0.0, 0.0)) - up * 0.03, hcen + Vector((sx, 0.0, 0.0)) - up * 0.06], 0.012, CABD, sides=8) HR = [(-0.16, 0.035), (-0.16, -0.035), (-0.145, -0.075), (-0.115, -0.070), (-0.090, -0.035), (-0.030, -0.028)] HR = HR + [(-x, d) for x, d in reversed(HR)] rings = [] for t in (-0.095, -0.085, 0.085, 0.095): k = 0.85 if abs(t) > 0.09 else 1.0 rings.append([hcen + up * t + Vector((x * k, 0, 0)) - fwd * (d * k) for x, d in HR]) loft_rings(cab, rings, SEAT, ladder=True) # armrests: the outboard one a pad with a control panel and the side stick, the inboard one a plain pad for side in (1, -1): ax = cx + side * (0.28 + 0.035) qbox(cab, (0.07, 0.38, 0.05), Vector((ax, yc - 0.02, Z_CUSH + 0.16)), SEAT, bevel=0.018) qbox(cab, (0.05, 0.08, 0.15), Vector((ax, yc + 0.13, Z_CUSH + 0.07)), CABD, bevel=0.012) if side == sg: pc = Vector((ax, yc - 0.17, Z_CUSH + 0.20)) qbox(cab, (0.085, 0.12, 0.045), pc, CABD, rot=(math.radians(-20), 0, 0), bevel=0.008) qbox(cab, (0.060, 0.080, 0.006), pc + Vector((0, -0.006, 0.024)), SCREEN, rot=(math.radians(-20), 0, 0)) qcyl(cab, 0.016, 0.12, Vector((ax, yc - 0.06, Z_CUSH + 0.245)), CABD, segs=8, r2=0.012) qsphere(cab, Vector((ax, yc - 0.06, Z_CUSH + 0.31)), (0.022, 0.022, 0.026), CABD, cuts=1) SEAT_EYES[sg] = Vector((cx, Y_EYE, EYE_Z)) # a footrest ahead of the seat, on the deck's front edge qbox(cab, (0.36, 0.05, 0.18), Vector((cx * 0.92, Y_DECK1 + 0.10, Z_DECK + 0.12)), CABD, rot=(math.radians(-35), 0, 0), bevel=0.01) # ---- the console column between the seats (SECTION A-A: up from the deck between the seats to the centre overhead screen), # its CONTROL BOX with two pads, one angled toward each seat (the section's two-pad box, z -0.58 .. -0.19) CH = CABIN["console_half"] Z_CON = CABIN["console_top_z"] yc0, yc1 = Y_SEAT_BACK - 0.08, Y_SEAT_BACK - 0.44 ycm = 0.5 * (yc0 + yc1) qbox(cab, (0.20, abs(yc1 - yc0) * 0.7, Z_CON - 0.06 - Z_DECK), Vector((0, ycm, 0.5 * (Z_CON - 0.06 + Z_DECK))), CABD, bevel=0.02) zb0, zb1 = CABIN["console_box_z"] qbox(cab, (2 * CH, abs(yc1 - yc0), zb1 - zb0), Vector((0, ycm, 0.5 * (zb0 + zb1))), CABD, bevel=0.02) for sg in (1, -1): # the two pads: sloped faces toward each seat, two screens on each pc = Vector((sg * (CH - 0.05), ycm, zb1 + 0.030)) rot = (0.0, sg * math.radians(25), 0.0) qbox(cab, (0.15, abs(yc1 - yc0) * 0.82, 0.05), pc, CABD, rot=rot, bevel=0.010) nrm = Vector((sg * math.sin(math.radians(25)), 0.0, math.cos(math.radians(25)))) qbox(cab, (0.11, 0.16, 0.006), pc + nrm * 0.026 + Vector((0, -0.07, 0)), SCREEN, rot=rot) qbox(cab, (0.11, 0.10, 0.006), pc + nrm * 0.026 + Vector((0, 0.08, 0)), SCREEN, rot=rot) qbox(cab, (0.012, 0.22, 0.20), Vector((sg * (0.10 + 0.007), ycm, Z_CON - 0.32)), SCREEN, bevel=0.003) # column side screens qbox(cab, (0.16, 0.012, 0.30), Vector((0.0, ycm - abs(yc1 - yc0) * 0.35 - 0.007, Z_CON - 0.30)), SCREEN, bevel=0.003) # its front # ---- the dashboard under the front window: a desk across the nose with a SCREEN BANK facing each pilot (critique round 1 M6: # the concept's windows glow with screens): three per pilot, the middle one larger, tilted back 40 deg, and one in the middle Y_DB = Yi(866.0) z_db_top = -0.13 w_db = min(inner_x(z_db_top, Y_DB - 0.15), inner_x(-0.30, Y_DB - 0.15)) - 0.02 zf_ = max(inner_bot(x_, y_, 0.02) for x_ in (-w_db, -0.5 * w_db, 0.0, 0.5 * w_db, w_db) for y_ in (Y_DB - 0.15, Y_DB, Y_DB + 0.15)) qbox(cab, (2 * w_db, 0.30, z_db_top - zf_), Vector((0, Y_DB, 0.5 * (z_db_top + zf_))), CABD, bevel=0.02) TILT = math.radians(40) SCR_N = Vector((0.0, math.cos(TILT), math.sin(TILT))) # a dashboard screen's face: aft and up, toward the pilots for sg in (1, -1): for dx, w, h in ((-0.17, 0.15, 0.11), (0.0, 0.19, 0.13), (0.17, 0.15, 0.11)): x_ = sg * (SEAT_X * 0.92) + dx if abs(x_) + 0.5 * w > w_db - 0.01: continue c = Vector((x_, Y_DB + 0.06, z_db_top + 0.045)) qbox(cab, (w + 0.02, 0.025, h + 0.02), c, CABD, rot=(TILT, 0, 0), bevel=0.004) qbox(cab, (w, 0.004, h), c + SCR_N * 0.0135, SCREEN, rot=(TILT, 0, 0)) c = Vector((0.0, Y_DB + 0.04, z_db_top + 0.04)) qbox(cab, (0.20, 0.025, 0.11), c, CABD, rot=(TILT, 0, 0), bevel=0.004) qbox(cab, (0.18, 0.004, 0.09), c + SCR_N * 0.0135, SCREEN, rot=(TILT, 0, 0)) qbox(cab, (0.30, 0.14, 0.03), Vector((0, Y_DB + 0.13, z_db_top + 0.015)), CABD, bevel=0.006) # ---- screen banks along the bottom of each side window (the concept's lit side panels): a sloped ledge on the wall under the # glass, its screens facing inboard and up toward the pilots for sg in (1, -1): for yq in (Yi(852.0), Yi(864.0), Yi(876.0)): zl = -0.42 # (round 2: 8 cm lower, clear of the side window's longer flange) xw = min(inner_x(zl - 0.08, yq), inner_x(zl + 0.08, yq)) - 0.075 if xw < 0.3: continue rot = (0.0, sg * math.radians(35), 0.0) nrm = Vector((-sg * math.cos(math.radians(35)), 0.0, math.sin(math.radians(35)))) c = Vector((sg * xw, yq, zl)) qbox(cab, (0.03, 0.17, 0.15), c, CABD, rot=rot, bevel=0.005) qbox(cab, (0.004, 0.15, 0.13), c + nrm * 0.0165, SCREEN, rot=rot) # ---- overhead screens (the section's three: two over the seats, one in the middle), hanging from the ceiling ahead of the # eyes, twice the area they had (critique round 1 M6), the centre one on top of the console column Y_OH = Y_EYE - 0.24 zo0, zo1 = CABIN["overhead_z"] zo0 = zo0 - 0.02 zo1 = zo1 + 0.10 OH_ROT = (math.radians(-28), 0, 0) OH_N = Vector((0.0, math.cos(math.radians(28)), -math.sin(math.radians(28)))) # their faces: aft and down for sg in (1, -1): xo0 = SEAT_X - 0.28 xo1 = min(SEAT_X + 0.30, inner_x(zo1, Y_OH, 0.03)) c = Vector((sg * 0.5 * (xo0 + xo1), Y_OH, 0.5 * (zo0 + zo1))) qbox(cab, (xo1 - xo0, 0.03, zo1 - zo0), c, CABD, rot=OH_ROT, bevel=0.008) qbox(cab, (xo1 - xo0 - 0.04, 0.006, zo1 - zo0 - 0.04), c + OH_N * 0.018, SCREEN, rot=OH_ROT) # a status display on its forward face too (the concept's windows glow with screens seen from outside, ahead of the cabin) qbox(cab, (xo1 - xo0 - 0.06, 0.006, zo1 - zo0 - 0.08), c - OH_N * 0.018, SCREEN, rot=OH_ROT) zt_ = inner_top(sg * SEAT_X, Y_OH, 0.0) qcyl(cab, 0.012, zt_ - zo1 + 0.02, Vector((sg * SEAT_X, Y_OH, 0.5 * (zt_ + zo1))), CABD, segs=8) hc = CABIN["overhead_centre_half"] zc0, zc1 = Zs(112.0), Zs(103.0) + 0.06 c = Vector((0, Y_OH - 0.05, 0.5 * (zc0 + zc1))) qbox(cab, (2 * hc, 0.03, zc1 - zc0), c, CABD, rot=OH_ROT, bevel=0.008) qbox(cab, (2 * hc - 0.04, 0.006, zc1 - zc0 - 0.03), c + OH_N * 0.018, SCREEN, rot=OH_ROT) qbox(cab, (2 * hc - 0.06, 0.006, zc1 - zc0 - 0.05), c - OH_N * 0.018, SCREEN, rot=OH_ROT) zt_ = inner_top(0.0, Y_OH - 0.05, 0.0) qcyl(cab, 0.012, zt_ - zc1 + 0.02, Vector((0, Y_OH - 0.05, 0.5 * (zt_ + zc1))), CABD, segs=8) # ---- a dim cyan light strip along the ceiling (the concept's lit cabin reads through the acrylic): a slim bar on the centre # line from over the dashboard back to the overhead screens, and one along each side over the windows (M_Grouper_CabinGlow) for x_, y0_, y1_ in ((0.0, Yi(880.0), Y_OH - 0.12), (0.36, Yi(872.0), Yi(822.0)), (-0.36, Yi(872.0), Yi(822.0))): pts = [] for q in range(7): y_ = y0_ + (y1_ - y0_) * q / 6 pts.append(Vector((x_, y_, inner_top(x_, y_, 0.0) - 0.022))) tube(cab, pts, 0.012, CABGLOW, sides=8) # ---- wall control panels outboard of the seats zw0, zw1 = CABIN["wall_panel_z"] for sg in (1, -1): yw = Y_SEAT_BACK - 0.30 xw = min(inner_x(zw0, yw), inner_x(zw1, yw)) - 0.06 qbox(cab, (0.08, 0.40, zw1 - zw0), Vector((sg * xw, yw, 0.5 * (zw0 + zw1))), CABD, bevel=0.012) qbox(cab, (0.006, 0.30, 0.22), Vector((sg * (xw - 0.043), yw, 0.5 * (zw0 + zw1) + 0.08)), SCREEN) # ---- the ladder under the hatch (on the bore's aft side, standing off the rear bulkhead) Y_LAD = Y_CL + 0.40 z_lt = 0.98 for sx in (-0.20, 0.20): tube(cab, [Vector((sx, Y_LAD, Z_DECK)), Vector((sx, Y_LAD, z_lt))], 0.018, CABD, sides=8) yb_ = Yi(X_BULK) - 0.012 rrect_bar(cab, Vector((sx, Y_LAD + 0.015, z_lt - 0.10)), Vector((sx, yb_, z_lt - 0.10)), 0.012, 0.012, 0.004, CABD) rrect_bar(cab, Vector((sx, Y_LAD + 0.015, Z_DECK + 0.40)), Vector((sx, yb_, Z_DECK + 0.40)), 0.012, 0.012, 0.004, CABD) for q in range(7): z = Z_DECK + 0.30 + 0.30 * q tube(cab, [Vector((-0.20, Y_LAD, z)), Vector((0.20, Y_LAD, z))], 0.014, CABD, sides=8) # ---- critique round 2 (M3): screens where the concept camera's sight lands. Through the front glass it lands on the port # wall aft of the side window, round the door and aft of it, and on the rear bulkhead's port corner; through the starboard # window on the port seat and the rear bulkhead (build/checks/concept_sight.json). So: # * a bank of four screens on the rear bulkhead's forward face, above the deck's height of the seat backs, either side of # the ladder; # * on both side walls an inboard-facing screen panel between the side window and the door, and one aft of the door, each # tilted with the wall; # * a forward-tilted top row along the dashboard's front edge (three slim status screens facing up and forward), kept # under z -0.07 so the pilots' forward view through the front glass is unchanged (gr_view_check.py). def _rot_apply(rot, v): return Euler(rot, 'XYZ').to_matrix() @ v def screen_panel(c, size, rot, face, mat_bezel=CABD, inset=0.02): """a dark bezel (size: thickness along `face` first in its local frame) with a screen on its `face` side (local axis)""" qbox(cab, size, c, mat_bezel, rot=rot, bevel=0.006) n = _rot_apply(rot, Vector(face)) t = abs(Vector(face).dot(Vector(size))) s2 = tuple((0.004 if abs(f) > 0.5 else max(0.02, d - inset)) for f, d in zip(face, size)) qbox(cab, s2, c + n * (0.5 * t + 0.0015), SCREEN, rot=rot) # the rear bulkhead (the lining's end cap at I_BULK): its forward-most point _bk = [BASE_L[I_BULK][k] - NRM_L[I_BULK][k] * sink_of(I_BULK) for k in range(0, len(BASE_L[I_BULK]), 2)] Y_BK = min(p.y for p in _bk) BULK_SCREENS = [] for xc_, w_ in ((-0.80, 0.30), (-0.40, 0.34), (0.40, 0.34), (0.80, 0.30)): z0_, z1_ = -0.02, 0.30 lim = min(inner_x(z_, Y_BK - 0.05, 0.03) for z_ in (z0_, z1_)) if abs(xc_) + 0.5 * w_ > lim: # fitted inside the lining at its narrowest w_ = 2 * (lim - abs(xc_)) if lim - abs(xc_) > 0.11 else 0.0 if w_ < 0.2: continue c_ = Vector((xc_, Y_BK - 0.011, 0.5 * (z0_ + z1_))) screen_panel(c_, (w_, 0.030, z1_ - z0_), (0, 0, 0), (0, -1, 0)) BULK_SCREENS.append([round(xc_, 2), round(w_, 2)]) # the side walls: between the side window's aft end and the door, and aft of the door, z -0.04 .. 0.40, tilted with the wall WALL_SCREENS = [] for sg in (1, -1): for ya_, yb_ in ((SG_YB + WIN_FL + 0.04, DOOR_YA - 0.10), (DOOR_YB + 0.08, Y_BK - 0.06)): if yb_ - ya_ < 0.22: continue z0_, z1_ = -0.04, 0.40 xa_ = min(inner_x(z0_ + 0.01, y_, 0.016) for y_ in (ya_, 0.5 * (ya_ + yb_), yb_)) xb_ = min(inner_x(z1_ - 0.01, y_, 0.016) for y_ in (ya_, 0.5 * (ya_ + yb_), yb_)) tilt = math.atan2(xa_ - xb_, z1_ - z0_) rot_ = (0.0, -sg * tilt, 0.0) c_ = Vector((sg * (0.5 * (xa_ + xb_) - 0.015), 0.5 * (ya_ + yb_), 0.5 * (z0_ + z1_))) screen_panel(c_, (0.030, yb_ - ya_, (z1_ - z0_) / math.cos(tilt)), rot_, (-sg, 0, 0)) WALL_SCREENS.append([sg, round(ya_, 3), round(yb_, 3)]) # the dashboard's forward-tilted top row: three slim status screens on its front edge, facing up and forward (45 deg) TOPROW = [] for xc_ in (-0.36, 0.0, 0.36): rot_ = (math.radians(-45.0), 0.0, 0.0) c_ = Vector((xc_, Y_DB - 0.135, z_db_top + 0.010)) if abs(xc_) + 0.13 > w_db - 0.02: continue screen_panel(c_, (0.26, 0.020, 0.10), rot_, (0, -1, 0), inset=0.016) TOPROW.append(xc_) REPORT["cabin_screens_round2"] = {"bulkhead": BULK_SCREENS, "walls": WALL_SCREENS, "dash_top_row_x": TOPROW, "bulkhead_y": round(Y_BK, 3)} REPORT["cabin"] = {"seats_x": SEAT_X, "eyes": {("L" if s > 0 else "R"): [round(c, 3) for c in v] for s, v in SEAT_EYES.items()}, "deck_z": round(Z_DECK, 3)}