""" The Engineer pack's corridor system as parametric units, cut from its "White" demo map. How the pack is built (measured from the demo, Blender metres, z relative to the door threshold): * A CAR is a run of 3.6 m bays, 5.2 m wide outside (4.36 inside), walls from -0.9 to +3.9, ceiling at +4.2. Two kinds: "sunken" (white floor 0.9 below the threshold, a five-step stair at each door) and "flush" (a glass floor over pipes at threshold level). * A CUBE is the 3.6 m junction room, floor at threshold level, 5.6 m high, one side each either door or wall. * A door plane lies in a 0.7 m gap between two structures; the doorway frame, the door leaf and what is mounted on the frame (CONN) fill that gap. Door planes are L = 3.6 n + 0.7 apart along a car, 4.3 across a cube. Units are returned in a local frame: origin on door plane A at threshold level, +X along the car. """ import math from mathutils import Matrix, Vector from units import BAY, DOOR_GAP, DOORSET, LOOSE, Demo, Item, Unit, frame WHITE_MAP = "ModSci_Engineer__Example_Dynamic_White" Z_DOOR = 0.2 # threshold level in the demo map CAR_HALF = 2.6 # outer half width of a car WALL_T = 0.42 # door plane A (x, y), heading from A to B, in the demo map CAR_SRC = { "white": ((-42.65, -15.6), 0.0), # sunken, white floor, grey padded walls "gold": ((-26.85, -15.6), 0.0), # sunken, white floor, gold foil walls "red": ((-13.2, -13.45), 90.0), # flush glass floor, red light "blue": ((-11.05, 0.2), 0.0), # flush glass floor, blue-white light } SUNKEN = ("white", "gold") CUBE_SRC = ((-31.15, -15.6), 0.0) # the straight-through cube between the white and the gold car CONN_SRC = ((-26.85, -15.6), 0.0) # the door between that cube and the gold car STRUCT_PREFIX = ("SM_Wall_HB", "SM_Ceiling_HB", "SM_Floor_A", "SM_GlassFloor", "SM_FloorPipes", "SM_Doorway_SideBrace", "SM_Truss", "SM_AirDuct", "SM_Pipe_Medium", "SM_Pipe_Small", "SM_Stairs_A", "SM_Ceiling_Wire", "SM_Ceiling_CoatedPipe", "SM_Wall_Puffy_A", "SM_Intersection") # parts of the Interiors hall that reach into the first car's region HALL_PREFIX = ("SM_Doorway_Small_A2", "SM_Doorway_Small_A4", "SM_Wall_Doorway", "SM_Floor_B", "SM_Floor_C", "SM_Rail_Pad", "SM_Ceiling_Main", "SM_Floor_Main", "SM_Floor_Below", "SM_PowerCells", "SM_EnergyNode") DOOR_END_FLOOR = ("SM_Stairs_A", "SM_Floor_A_End", "SM_GlassFloor_Door", "SM_GlassFloor_Door_B") CONN_HALF = 0.62 # what is mounted on a doorway lies within this distance of the door plane def is_struct(it): return it.kind == "mesh" and it.name.startswith(STRUCT_PREFIX) def is_door_actor(it): return it.actor_class.startswith("Door_A") def rot_z(deg): return Matrix.Rotation(math.radians(deg), 4, "Z") class Engineer(object): def __init__(self, demo=None): self.demo = demo or Demo(WHITE_MAP) self._car = {} self.conn = self._make_conn() self.cube_base, self.cube_wall = self._make_cube() # -- sources ------------------------------------------------------------------------------------------------ def _src_car(self, kind): if kind in self._car: return self._car[kind] (ax, ay), yaw = CAR_SRC[kind] f = frame(ax, ay, Z_DOOR, yaw) L = 3 * BAY + 2 * DOOR_GAP def pred(it, p): if not (-0.05 <= p.x <= L + 0.05 and abs(p.y) <= 3.15): return False if is_door_actor(it) or (it.kind == "mesh" and (it.name in DOORSET or it.name.startswith(HALL_PREFIX))): return False if it.kind == "mesh" and it.key.split("/")[2] == "ModSciInteriors" and not it.name.startswith(("SM_Wall_Puffy", "SM_Light", "SM_Electric")): return False d = min(abs(p.x), abs(p.x - L)) if d < CONN_HALF and not is_struct(it): return False # mounted on a doorway: belongs to the connection return True u = self.demo.unit(f, pred) self._car[kind] = u return u def _make_conn(self): (ax, ay), yaw = CONN_SRC f = frame(ax, ay, Z_DOOR, yaw) def pred(it, p): if abs(p.y) > 2.3 or abs(p.x) > CONN_HALF: return False if it.kind == "mesh" and it.name in DOORSET: return True if is_door_actor(it): return True return not is_struct(it) return self.demo.unit(f, pred) def _make_cube(self): (ax, ay), yaw = CUBE_SRC f = frame(ax, ay, Z_DOOR, yaw) L = BAY + 2 * DOOR_GAP def pred(it, p): if not (-0.05 <= p.x <= L + 0.05 and abs(p.y) <= 2.6): return False if is_door_actor(it) or (it.kind == "mesh" and it.name in DOORSET): return False d = min(abs(p.x), abs(p.x - L)) if d < CONN_HALF and not is_struct(it): return False if it.kind == "mesh" and it.name.startswith(("SM_GlassFloor_Door", "SM_Stairs_A", "SM_Floor_A", "SM_Doorway_SideBrace", "SM_Wall_HB", "SM_Truss", "SM_Ceiling_CoatedPipe", "SM_Pipe_", "SM_AirDuct")): return False # end pieces of the neighbouring cars return True u = self.demo.unit(f, pred) walls = [it for it in u.items if it.kind == "mesh" and it.name.startswith("SM_Intersection_Wall")] base = Unit([it for it in u.items if it not in walls]) # one wall, re-expressed about the cube centre as "the wall on the +Y side" c = Matrix.Translation((L / 2, 0, 0)) wall = None for it in walls: if it.center.y > 1.0: wall = it.moved(c.inverted()) if wall is None and walls: wall = walls[0].moved(rot_z(180.0) @ c.inverted()) return base, wall # -- cars --------------------------------------------------------------------------------------------------- @staticmethod def length(n): return n * BAY + 2 * DOOR_GAP def car(self, kind, n, door_a=True, door_b=True, side_doors=(), props="all"): """Items of an n-bay car (n >= 2), frame on door plane A. door_a / door_b: False closes that end with a wall panel instead of leaving it open for a CONN. side_doors: [(bay index 0..n-1, side +1 (local +Y) or -1)]: that wall panel is left out for a CONN; in a sunken car the door's stair and riser are added inside. props: "all" (as the author dressed it; loose things are not repeated in added bays), "tidy" (nothing loose on the floor), "bare" (structure, lights and lettering only).""" bare = props == "bare" tidy = props == "tidy" src = self._src_car(kind) L3 = self.length(3) Ln = self.length(n) out = [] sunken = kind in SUNKEN side = {(b, s) for b, s in side_doors} def bay_of(x): return max(0, min(2, int(math.floor((x - DOOR_GAP + 1e-4) / BAY)))) end_wall_src = None for it in src.items: x = it.center.x if bare and it.kind == "mesh" and not is_struct(it): continue b = bay_of(x) if it.kind == "mesh" and it.name.startswith("SM_Wall_HB_A") and end_wall_src is None and b == 1: end_wall_src = it if b == 0: targets = [(0, 0.0)] elif b == 2: targets = [(n - 1, (n - 3) * BAY)] else: targets = [(k, (k - 1) * BAY) for k in range(1, n - 1)] for k, dx in targets: near_a = k == 0 and x < DOOR_GAP + 1.25 near_b = k == n - 1 and x > L3 - DOOR_GAP - 1.25 if it.kind == "mesh" and it.name.startswith(DOOR_END_FLOOR): if (near_a and not door_a) or (near_b and not door_b): continue if it.kind == "mesh" and it.name in LOOSE and (tidy or (b == 1 and dx != 0.0)): continue # loose things: never repeated in added bays, none at all when tidy s = 1 if it.center.y > 0 else -1 if (k, s) in side: if it.kind == "mesh" and (it.name.startswith(("SM_Wall_HB_A", "SM_Wall_Puffy_A")) and not it.name.endswith("Edge")): continue # the wall panel the side door replaces if abs(it.center.y) > 1.72 and not is_struct(it): continue # and what hangs on it out.append(it.moved(Matrix.Translation((dx, 0, 0))) if dx else it) # closed ends: a wall panel across, between the two corner braces, inside the structure's end plane (so two # rooms can stand end to end) if end_wall_src is not None: for closed, x_plane, yaw in ((not door_a, DOOR_GAP, -90.0), (not door_b, Ln - DOOR_GAP, 90.0)): if not closed: continue # the panel mesh spans local x -3.6..0, its room face looks along local +y (0.42 thick) m = Matrix.Translation((x_plane, (1.8 if yaw > 0 else -1.8), -0.9)) @ rot_z(yaw) out.append(Item("mesh", end_wall_src.key, m, "End wall", "", dict(end_wall_src.data), m.translation.copy())) # stairs and riser inside a sunken car at each side door (taken from the car's own door end A) if sunken and side: end_set = [it for it in src.items if it.kind == "mesh" and it.name.startswith(("SM_Stairs_A", "SM_Floor_A_End", "SM_GlassFloor_Door")) and it.center.x < DOOR_GAP + 1.25 and not it.name.startswith("SM_GlassFloor_Door_B")] for b, s in side: xc = DOOR_GAP + (b + 0.5) * BAY # the car's door plane A (x = 0) becomes the wall line y = s * 2.6; into the car = -s * y t = Matrix.Translation((xc, s * CAR_HALF, 0.0)) @ rot_z(-90.0 * s) for it in end_set: out.append(it.moved(t)) return Unit(out) # -- cubes -------------------------------------------------------------------------------------------------- def cube(self, doors="WE", props="all"): """Junction cube, frame on its west door plane. doors: which of W E N S are doors (the rest get a wall).""" L = BAY + 2 * DOOR_GAP out = [it for it in self.cube_base.items if not (it.kind == "mesh" and ((props == "bare" and not is_struct(it)) or (props == "tidy" and it.name in LOOSE)))] c = Matrix.Translation((L / 2, 0, 0)) if self.cube_wall is not None: for sidename, yaw in (("N", 0.0), ("W", 90.0), ("S", 180.0), ("E", 270.0)): if sidename not in doors: out.append(self.cube_wall.moved(c @ rot_z(yaw))) return Unit(out) @staticmethod def cube_door_frames(f, doors="WE"): """Frames of a cube's door planes (for CONNs): f = the cube's own frame (west door plane).""" L = BAY + 2 * DOOR_GAP res = {} for name, m in (("W", Matrix.Identity(4)), ("E", Matrix.Translation((L, 0, 0))), ("N", Matrix.Translation((L / 2, L / 2, 0)) @ rot_z(90.0)), ("S", Matrix.Translation((L / 2, -L / 2, 0)) @ rot_z(-90.0))): if name in doors: res[name] = f @ m return res