diff --git a/docs/fleet-calculator.html b/docs/fleet-calculator.html index b77af04..9d55642 100644 --- a/docs/fleet-calculator.html +++ b/docs/fleet-calculator.html @@ -310,7 +310,8 @@ html[data-theme=light] .iso-wrap { .iso-seg { display: inline-flex; } .iso-seg button { border-radius: 0; } .iso-seg button:first-child { border-radius: 5px 0 0 5px; } -.iso-seg button:last-child { border-radius: 0 5px 5px 0; border-left: 0; } +.iso-seg button:last-child { border-radius: 0 5px 5px 0; } +.iso-seg button + button { border-left: 0; } .iso-seg button[aria-pressed=true] { background: var(--iso-btnon); border-color: var(--iso-accent); color: var(--iso-btnfg); font-weight: 600; } .iso-compass { display: inline-flex; align-items: center; gap: 2px; padding: 3px 7px; cursor: default; } .iso-compass svg { width: 20px; height: 20px; } @@ -388,7 +389,8 @@ html[data-theme=light] .iso-wrap { .iso-speaker { --b: #2b2f37; } .iso-shrink { --b: #9aa4b2; } .iso-cass { --b: #333a46; } .iso-cdtower { --b: #c7ccd4; } .iso-forklift { --b: #e3a400; } .iso-pump { --b: #3f78c0; } .iso-motor { --b: #59606b; } .iso-accu { --b: #c9473a; } .iso-comp { --b: #4f8d58; } .iso-oil { --b: #b88a2a; } .iso-belt { --b: #2b313b; } .iso-hx { --b: #8aa0b8; } -.iso-lathe { --b: #3c4658; } .iso-stair { --b: #9aa0aa; } .iso-sign { --b: var(--iso-signline); } .iso-fanbox { --b: #6b7585; } +.iso-lathe { --b: #3c4658; } .iso-stair { --b: #9aa0aa; } .iso-lift { --b: #737b88; } .iso-rail2 { --b: var(--iso-steel); } +.iso-server { --b: #27303f; } .iso-shower { --b: #5f9fd6; } .iso-wc { --b: #8e9aa9; } .iso-sign { --b: var(--iso-signline); } .iso-fanbox { --b: #6b7585; } .iso-bushing { --b: #c9b8a4; } .iso-pipes { --b: #c9473a; } .iso-pipec { --b: #3f78c0; } .iso-pipec2 { --b: #6aa8ff; } .iso-svg .iso-headc { fill: var(--iso-head); } .iso-svg .iso-fence, .iso-svg .iso-fence.iso-t { fill-opacity: var(--iso-fenceo); --b: var(--iso-fence); } @@ -457,6 +459,19 @@ html[data-theme=light] .iso-svg .iso-lamp { fill: #c9ced6; } .iso-z-lobby, .iso-z-canteen { --b: color-mix(in srgb, var(--iso-office) 70%, var(--iso-floor)); } .iso-z-cut, .iso-z-listen { --b: color-mix(in srgb, var(--iso-floor) 72%, #6b4a8e); } .iso-z-arch { --b: color-mix(in srgb, var(--iso-floor) 76%, #6aa8ff); } +.iso-z-core { --b: color-mix(in srgb, var(--iso-floor) 88%, #c9ced6); } +.iso-z-hall { --b: color-mix(in srgb, var(--iso-office) 55%, var(--iso-floor)); } +.iso-z-staff2 { --b: color-mix(in srgb, var(--iso-floor) 80%, #7fa8d8); } +.iso-z-kitchen { --b: color-mix(in srgb, var(--iso-floor) 76%, #c89a6a); } +.iso-z-wc { --b: color-mix(in srgb, var(--iso-floor) 80%, #8fbfb4); } +.iso-z-offices { --b: color-mix(in srgb, var(--iso-floor) 76%, #8fb0e0); } +.iso-z-meet, .iso-z-meet2 { --b: color-mix(in srgb, var(--iso-floor) 74%, #7fcf98); } +.iso-z-it { --b: color-mix(in srgb, var(--iso-floor) 70%, #4f7fc0); } +.iso-z-pantry { --b: color-mix(in srgb, var(--iso-floor) 74%, #e0b070); } +.iso-z-flex { --b: color-mix(in srgb, var(--iso-floor) 80%, #b8a8e0); } +.iso-z-print { --b: color-mix(in srgb, var(--iso-floor) 74%, #d08ad0); } +.iso-z-stock { --b: color-mix(in srgb, var(--iso-floor) 76%, #e0cc90); } +.iso-z-packmat { --b: color-mix(in srgb, var(--iso-floor) 78%, #90d0a0); } .iso-svg .iso-part { fill: none; stroke: var(--iso-part); stroke-width: .9; stroke-linecap: square; } .iso-svg .iso-mark { fill: none; stroke: var(--iso-mark); stroke-width: .7; stroke-dasharray: 3 2; } .iso-svg .iso-trench { fill: var(--iso-trench); stroke: none; } @@ -466,6 +481,13 @@ html[data-theme=light] .iso-svg .iso-lamp { fill: #c9ced6; } .iso-svg .iso-slot.iso-empty { stroke-dasharray: 3 2.4; fill: var(--iso-mark); fill-opacity: .06; } .iso-svg .iso-cell { fill: none; stroke: var(--iso-hivis); stroke-width: .45; stroke-dasharray: 1.4 1.4; opacity: .8; } .iso-svg .iso-bay { fill: var(--iso-mark); fill-opacity: .1; stroke: var(--iso-mark); stroke-width: .5; stroke-dasharray: 2 2; } +.iso-svg .iso-void { fill: var(--iso-open); stroke: none; } +.iso-svg .iso-lining { fill: none; stroke: #8a6fb0; stroke-width: 1.4; opacity: .75; } + +/* 1F level: single-storey buildings stay as ghosted ground-floor context, standing still */ +.iso-svg .iso-dim { opacity: .4; } +html[data-theme=light] .iso-svg .iso-dim { opacity: .5; } +.iso-svg .iso-dim .iso-a { animation: none !important; } /* tags (counter-scaled by --iso-k so they keep their screen size) */ .iso-tag path { fill: var(--iso-tagbg); stroke: var(--iso-tagline); stroke-width: 1; } @@ -552,11 +574,15 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } } @media (max-width: 640px) { .iso-stage { aspect-ratio: 1 / 1; min-height: 320px; } - .iso-bar { gap: 4px; top: 6px; left: 6px; right: 6px; } - .iso-bar button { padding: 4px 7px; min-width: 28px; min-height: 30px; } - .iso-compass { padding: 3px 4px; min-height: 30px; } + .iso-bar { gap: 3px; top: 6px; left: 6px; right: 6px; } + .iso-bar button { padding: 4px 6px; min-width: 26px; min-height: 30px; } + .iso-compass { padding: 3px; min-height: 30px; } .iso-compass b { display: none; } } +/* the level selector, rotation, zoom and fit stay on one line down to ~360 px; the compass gives way first */ +@media (max-width: 370px) { + .iso-compass { display: none; } +}
@@ -596,7 +622,8 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }The current plant configuration (preset or the switches below) as a tile-game view of the site: presses, stamper line, cutting room, utility yard with its boiler and chiller modules, solar and battery. - Switch between the roof and the inside, rotate, zoom and tap anything for its figures. Schematic, + Switch between the ground floor (GF), the upper floors of the office and the finishing and print + block (1F) and the roofs, rotate, zoom and tap anything for its figures. Schematic, not to scale where the vendors publish no footprint.
@@ -884,6 +911,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } * * var view = CNXIso.mount(containerEl, cfg, { onPick: function (info) {} }); * view.update(cfg); // merges cfg over the current config and redraws + * view.setLevel("gf" | "1f" | "roof"); // ground-floor cutaway, upper floors, roofs * view.destroy(); * * Layout: the Payap scenario model (docs/site-candidates.md), the detached @@ -896,7 +924,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } (function (root) { "use strict"; - var VERSION = "1.0"; + var VERSION = "1.1"; var DEF = { duplex: 1, classic: 1, galvanics: true, cutting: true, metrology: "full", cassette: true, cdr: true, solar: "buy", pvKwp: 300, batteryKwh: 271, @@ -1155,12 +1183,16 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } function Scene(rot, uid) { this.P = new Proj(rot); this.uid = uid || 0; - this.ground = []; this.sp = []; this.tags = []; this.info = {}; this.flags = []; + this.ground = []; this.sp = []; this.tags = []; this.info = {}; this.flags = []; this.dim = false; } - Scene.prototype.sprite = function (id, cls) { var s = new Sprite(this, id, cls); this.sp.push(s); return s; }; + // while sc.dim is set (1F level: single-storey buildings as context) sprites are ghosted and carry no tags + Scene.prototype.sprite = function (id, cls) { + var s = new Sprite(this, id, this.dim ? (cls ? cls + " " : "") + "iso-dim" : cls); this.sp.push(s); return s; + }; Scene.prototype.g = function (s) { this.ground.push(s); }; Scene.prototype.inf = function (id, name, detail) { this.info[id] = { id: id, name: name, detail: detail || "" }; }; Scene.prototype.tag = function (x, y, z, text, prio, id, kind) { + if (this.dim) return; this.tags.push({ x: x, y: y, z: z, t: text, p: prio || 1, id: id || "", k: kind || "" }); }; @@ -1261,9 +1293,16 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } lost: hall.lost + p2.lost }; } - // street lamps: site roads, gate and car park; the utility yard gets two on its back fence - // (earlier positions at y 99.6 stood in the middle of the yard in front of the boiler hall) - var LAMPS = [[116.5, 15], [116.5, 32], [132, 45], [146.6, 62], [101, 62], [56, 15], [13, 34], [45, 113.2], [88, 113.2], [8.5, 97]]; + // paved areas trucks and cars drive on: [x0, y0, x1, y1] + var PAVED = { + gateRoad: [118, -6, 130, 37], apron: [100, 37, 148, 69], eastRoad: [136, 69, 148, 100], lane: [4, 94, 148, 100], + access: [54, 6, 118, 13], parking: [12, 3, 54, 36], yard: [19, 100, 136, 114] + }; + // street lamps stand on kerbs, fences and walls, never on a paved drive area (one list feeds the poles + // and the dusk light pools): gate road west kerb, the apron's west, south and east kerbs, the east road, + // the access road, the car park's west kerb, the lane's north kerb at the ramp, behind the utility yard. + // (Earlier spots stood in the middle of the truck apron, in the lane and in the yard.) + var LAMPS = [[116.5, 15], [116.5, 32], [136, 36.2], [148.8, 62], [99.2, 62], [148.8, 86], [56, 15], [11.2, 34], [50, 114.6], [88, 114.6], [8.5, 100.8]]; // ------------------------------------------------------------ PV placement var PV_ROWS = [1, 3, 5, 7, 10, 12, 14, 16, 19, 21]; @@ -1374,15 +1413,17 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } g.push(flatS(P, rect(-8, -16, 158, -6), 0, "iso-g-asph iso-t", "road")); g.push(linesS(P, [[-8, -11, 158, -11]], 0, "iso-dash")); g.push(linesS(P, [[-8, -6.3, 158, -6.3], [-8, -15.7, 158, -15.7]], 0, "iso-kerb")); - g.push(flatS(P, rect(118, -6, 130, 37), 0, "iso-g-asph iso-t", "apron")); - g.push(flatS(P, rect(100, 37, 148, 69), 0, "iso-g-conc iso-t", "apron")); - g.push(flatS(P, rect(136, 69, 148, 100), 0, "iso-g-asph iso-t", "apron")); - g.push(flatS(P, rect(4, 94, 148, 100), 0, "iso-g-asph iso-t", "lane")); - g.push(flatS(P, rect(54, 6, 118, 13), 0, "iso-g-asph iso-t", "road")); - g.push(flatS(P, rect(12, 3, 54, 36), 0, "iso-g-asph iso-t", "parking")); - g.push(flatS(P, rect(28, 36, 30.5, 42), 0, "iso-g-path iso-t", "office")); + var pr = function (k) { var r = PAVED[k]; return rect(r[0], r[1], r[2], r[3]); }; + g.push(flatS(P, pr("gateRoad"), 0, "iso-g-asph iso-t", "apron")); + g.push(flatS(P, pr("apron"), 0, "iso-g-conc iso-t", "apron")); + g.push(flatS(P, pr("eastRoad"), 0, "iso-g-asph iso-t", "apron")); + g.push(flatS(P, pr("lane"), 0, "iso-g-asph iso-t", "lane")); + g.push(flatS(P, pr("access"), 0, "iso-g-asph iso-t", "road")); + g.push(flatS(P, pr("parking"), 0, "iso-g-asph iso-t", "parking")); + // footpath from the car park to the office entrance (lobby at the west end, floor plans) + g.push(flatS(P, rect(15.5, 36, 18, 42), 0, "iso-g-path iso-t", "office")); g.push(flatS(P, rect(15.5, 57, 18.5, 69), 0, "iso-g-path iso-t", "link")); - g.push(flatS(P, rect(19, 100, 136, 114), 0, "iso-g-conc iso-t", "yard")); + g.push(flatS(P, pr("yard"), 0, "iso-g-conc iso-t", "yard")); g.push(flatS(P, rect(91.6, 100.6, 104.9, 112.2), 0.01, "iso-g-pad iso-t", "chillers")); if (al.p2show) g.push(flatS(P, rect(72, 57, 76, 69), 0, "iso-g-path iso-t", "phase2")); else g.push(flatS(P, rect(P2.x0, P2.y0, P2.x1, P2.y1), 0.01, "iso-reserve", "phase2")); @@ -1491,8 +1532,11 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } } // ------------------------------------------------------------ factory - function zoneList(cfg) { - return [ + // finishing and print block, 14 x 25 m on two levels at fx 92..106 (site-candidates.md, block floor plans): + // ground floor trim + optical, QC lab, stair + goods lift along the corridor, packaging behind; upper floor at +3.8 m + var BLK = { fx0: 92, fx1: 106, up: 3.8 }; + function zoneList(cfg, up) { + var z = [ [0, 0, 8, 10, "iso-z-staff", "staffentry"], [0, 10, 16, 25, "iso-z-shop", "workshop"], [8, 2.6, 16, 10, "iso-z-shop2", "workshop"], @@ -1502,14 +1546,14 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } [30, 2.6, 40, 20.5, "iso-z-colour", "colourstore"], [40, 2.6, 92, 20.5, "iso-z-press", "presshall"], [40, 12.4, 47.8, 20.5, "iso-z-plant", "plantroom"], - [16, 20.5, 92, 25, "iso-z-aisle", "aisle"], - [92, 2.6, 99, 10, "iso-z-trim", "trim"], - [99, 2.6, 106, 10, "iso-z-qc", "qc"], - [92, 10, 106, 25, "iso-z-pack", "packaging"], + [16, 20.5, up ? 92 : 106, 25, "iso-z-aisle", "aisle"], [106, 0, 120, 25, "iso-z-wh", "warehouse"], [120, 0, 128, 25, "iso-z-disp", "dispatch"], - [8, 0, 106, 2.6, cfg.corridor ? "iso-z-corr" : "iso-z-floor", "corridor"] + [8, 0, up ? 92 : 106, 2.6, cfg.corridor ? "iso-z-corr" : "iso-z-floor", "corridor"] ]; + if (!up) z.push([92, 2.6, 96, 7.9, "iso-z-trim", "trim"], [96, 2.6, 100, 7.9, "iso-z-qc", "qc"], [100, 2.6, 106, 7.9, "iso-z-core", "block-stair"], + [92, 7.9, 106, 20.5, "iso-z-pack", "packaging"]); + return z; } function factoryInfo(sc, cfg, al) { sc.inf("factory", "Factory, 128 x 25 m", @@ -1528,7 +1572,15 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } sc.inf("aisle", "Crane and forklift aisle, 4.5 m", "Along the back wall from the machine door to the fire wall: heavy moves need a floor-level path"); sc.inf("trim", "Trimming and 100 % optical inspection", "Edge trimmer and the optical station at the end of the record path (quality-automation.md)"); sc.inf("qc", "QC and listening", "Sampled audio comparison (about 1 in 50) and the listening point on the tour (quality-automation.md)"); - sc.inf("packaging", "Finishing and packaging", "Packing tables, shrink wrap and pallet wrapper; the finishing and print block is 14 x 25 m on two levels with the print room reserved upstairs"); + sc.inf("packaging", "Finishing and packaging", "Packing tables, shrink wrap and pallet wrapper, ~170 m2 on the ground floor of the finishing and print block (14 x 25 m on two levels, print room reserved upstairs)"); + sc.inf("block-stair", "Stair and goods lift", "Connect the two levels of the finishing and print block. The crane runway ends at the press hall, so loads here move by forklift and goods lift; heavy compound stays on the ground floor (site-candidates.md, crane-workshop-tours.md)"); + if (cfg.cassette) sc.inf("cassette", "Cassette duplication", "Bench line, ~40 to 80 m2 (project-timeline.md)"); + if (cfg.cdr) sc.inf("cdr", "CD-R duplication, short run", "Tower duplicator and printer bench, ~20 to 40 m2 (project-timeline.md)"); + sc.inf("printblock", "Finishing and print block, 14 x 25 m", + "Two levels: trim and optical inspection, the QC lab and packaging (~170 m2) on the ground floor; print room (reserve), print stock and packaging materials on the upper floor at +3.8 m (site-candidates.md)"); + sc.inf("printroom", "Print room (kept in reserve)", "Upper floor at +3.8 m. In-house printing is not decided; jackets come from a print partner today (site-candidates.md, production-timeline.md)"); + sc.inf("printstock", "Print stock", "Paper and board, upper floor at +3.8 m (site-candidates.md)"); + sc.inf("packmat", "Packaging materials", "Sleeves, inner bags, mailers and cartons, upper floor at +3.8 m (site-candidates.md)"); sc.inf("warehouse", "Warehouse behind the fire wall", "Three racking rows; fire wall EI 60/120; forklift territory, no crane"); sc.inf("dispatch", "Dispatch, two dock doors", "Pallets staged for the trucks on the apron"); sc.inf("workshop", "Workshop and machine bay, ~200 m2", "Service bay, benches, spares, 3D printers, a lathe and a small CNC mill; machine door 4.5 x 5.0 m with the ramp; the crane parks here (crane-workshop-tours.md)"); @@ -1595,11 +1647,11 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } ]); // pipe bridges and the covered crossing to water treatment are separate sprites } - function factoryInside(sc, cfg, al, pv) { - var x0 = X(0), y0 = Y(0), x1 = X(FL), y1 = Y(FW), P = sc.P, z = FFL; + function factoryInside(sc, cfg, al, pv, up) { + var x0 = X(0), y0 = Y(0), x1 = X(FL), y1 = Y(FW), P = sc.P, z = FFL, xe = up ? X(BLK.fx0) : X(105.8); var fl = sc.sprite("factory"); fl.box(x0, y0, x1, y1, 0, FFL, "iso-plinth"); - var zones = zoneList(cfg); + var zones = zoneList(cfg, up); zones.forEach(function (q) { fl.flat(rect(X(q[0]), Y(q[1]), X(q[2]), Y(q[3])), z + 0.005, q[4] + " iso-t", q[5]); }); var segs = []; zones.forEach(function (q) { @@ -1608,7 +1660,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } segs.push([a, b, c, b], [c, b, c, d], [c, d, a, d], [a, d, a, b]); }); fl.lines(segs, z + 0.01, "iso-part"); - fl.lines([[X(16), Y(20.5), X(92), Y(20.5)], [X(16), Y(24.6), X(92), Y(24.6)]], z + 0.01, "iso-mark"); + fl.lines([[X(16), Y(20.5), xe, Y(20.5)], [X(16), Y(24.6), xe, Y(24.6)]], z + 0.01, "iso-mark"); // service trench along the press rows fl.flat(rect(X(40), Y(11.6), X(92), Y(12.4)), z + 0.01, "iso-trench"); var gr = []; for (var gx = 40.5; gx < 92; gx += 1.2) gr.push([X(gx), Y(11.65), X(gx), Y(12.35)]); @@ -1632,7 +1684,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } // walls cut: back walls taller than front walls (cutaway) wallsCut(sc, "factory", x0, y0, x1, y1, z, 1.0, 3.0, 0.3, "iso-wall"); sc.sprite("factory").box(X(105.85), Y(0.3), X(106.15), Y(FW - 0.3), z, z + 2.2, "iso-firewall", { top: "iso-cut" }); - if (cfg.corridor) sc.sprite("corridor").box(X(8), Y(2.55), X(106), Y(2.65), z, z + 2.6, "iso-glassw"); + if (cfg.corridor) sc.sprite("corridor").box(X(8), Y(2.55), up ? X(BLK.fx0) : X(106), Y(2.65), z, z + 2.6, "iso-glassw"); // frame columns and crane runway (production compartment, west wall to the end of the press hall) [[0.3, 0.62], [24.38, 24.7]].forEach(function (yy) { var s = sc.sprite("crane", P.vis(0, yy[0] < 12 ? -1 : 1) ? "iso-ghost" : ""); @@ -1644,7 +1696,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } }); if (cfg.crane === "buy") craneM(sc, z, cfg); else sc.tag(X(46), Y(24.5), z + 6.5, "Crane-ready (rented)", 5, "crane"); - machinesFactory(sc, cfg, al, z); + machinesFactory(sc, cfg, al, z, up); // presses in the hall al.hall.forEach(function (sl) { if (sl.e) return; @@ -1653,8 +1705,8 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } else { classicM(sc, sl.n, cx, cy, z, cfg.running); sc.tag(cx, cy, z + 3.2, "Classic " + sl.n, 6, "classic-" + sl.n); } }); // record path to finishing (schematic conveyor along the corridor glass) - conveyor(sc, z, cfg); - if (cfg.corridor) visitors(sc, z, cfg); + conveyor(sc, z, cfg, up); + if (cfg.corridor && !up) visitors(sc, z, cfg); } function wallsCut(sc, id, x0, y0, x1, y1, z0, lo, hi, t, mat) { var P = sc.P; @@ -1684,8 +1736,8 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } hk.ge(); sc.tag(X(30), Y(12), z + 7.6, "Crane 5 t", 6, "crane"); } - function conveyor(sc, z, cfg) { - var P = sc.P, s = sc.sprite("conveyor"), xa = X(40.6), xb = X(95), y0 = Y(2.9), y1 = Y(3.35); + function conveyor(sc, z, cfg, up) { + var P = sc.P, s = sc.sprite("conveyor"), xa = X(40.6), xb = up ? X(BLK.fx0 - 0.2) : X(93), y0 = Y(2.9), y1 = Y(3.35); s.box(xa, y0, xb, y1, z + 0.72, z + 0.85, "iso-belt"); if (!cfg.running) return; var L = xb - xa - 1, v = P.dv(L, 0, 0), n = 8, dur = 16, i, p = P.s(xa + 0.5, (y0 + y1) / 2, z + 0.88), q = P.s(xa + 0.5, (y0 + y1) / 2, z + 0.88); @@ -1721,7 +1773,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } Object.keys(groups).forEach(function (k) { var g = groups[k]; s.open(g.A, g.B, g.l, g.cls); }); return s; } - function machinesFactory(sc, cfg, al, z) { + function machinesFactory(sc, cfg, al, z, up) { var P = sc.P, i; // staff entry: lockers sc.sprite("staffentry").box(X(0.6), Y(2), X(1.1), Y(8.5), z, z + 1.9, "iso-locker"); @@ -1803,34 +1855,9 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } sc.sprite("plantroom").box(X(45.4), Y(17.3), X(47.3), Y(19.0), z, z + 1.5, "iso-comp"); sc.sprite("plantroom").box(X(41), Y(16.0), X(43.6), Y(17.2), z, z + 1.2, "iso-oil"); sc.tag(X(44), Y(16.5), z + 3, "Plant room", 4, "plantroom"); - // finishing block: trimmer + optical station, QC, packaging, cassette, CD-R - var tr = sc.sprite("trim"); tr.box(X(95.3), Y(3.0), X(96.4), Y(4.1), z, z + 1.1, "iso-steel"); tr.disc(X(95.85), Y(3.55), z + 1.12, 0.26, "iso-vinylc"); - var op = sc.sprite("trim"); op.box(X(96.9), Y(2.95), X(98.6), Y(4.3), z, z + 1.5, "iso-optic"); op.cyl(X(97.75), Y(3.6), 0.35, z + 1.5, z + 1.8, "iso-dome"); op.led(X(97.75), Y(3.6), z + 1.82, "iso-ledg"); - sc.sprite("trim").box(X(96.9), Y(5.0), X(97.8), Y(5.8), z, z + 0.8, "iso-bin"); - sc.tag(X(97), Y(4), z + 2.6, "Trim + optical", 4, "trim"); - var qb = sc.sprite("qc"); qb.box(X(99.6), Y(7.6), X(105.6), Y(8.4), z, z + 0.85, "iso-bench"); - qb.seq([[X(101), Y(8), function (q) { q.box(X(100.4), Y(7.7), X(101.6), Y(8.3), z + 0.85, z + 1.0, "iso-instr"); q.disc(X(101), Y(8), z + 1.02, 0.24, "iso-vinylc"); }], - [X(103.5), Y(8), function (q) { q.box(X(103.1), Y(7.75), X(103.9), Y(8.25), z + 0.85, z + 1.45, "iso-instr2"); }], - [X(105), Y(8), function (q) { q.box(X(104.7), Y(7.8), X(105.3), Y(8.2), z + 0.85, z + 1.6, "iso-speaker"); }]]); - sc.tag(X(102.5), Y(6), z + 2.4, "QC + listening", 4, "qc"); - sc.sprite("packaging").box(X(93), Y(12), X(97), Y(13.2), z, z + 0.9, "iso-bench"); - sc.sprite("packaging").box(X(93), Y(15), X(97), Y(16.2), z, z + 0.9, "iso-bench"); - var sh = sc.sprite("packaging"); sh.box(X(99), Y(12), X(103.4), Y(13.1), z, z + 1.3, "iso-shrink"); sh.open([X(99), Y(12)], [X(103.4), Y(12)], [[1.4, 3.0, z + 0.3, z + 1.1]], "iso-open"); - var pw = sc.sprite("packaging"); pw.cyl(X(103.4), Y(19), 1.0, z, z + 0.15, "iso-steel"); pw.box(X(103.2), Y(17.8), X(103.6), Y(18.1), z, z + 2.4, "iso-steel"); - pw.box(X(102.85), Y(18.45), X(103.95), Y(19.55), z + 0.15, z + 1.4, "iso-carton"); - [[94.5, 19.5], [97, 19.5], [94.5, 17.8]].forEach(function (p) { sc.sprite("packaging").box(X(p[0] - 0.6), Y(p[1] - 0.5), X(p[0] + 0.6), Y(p[1] + 0.5), z, z + 1.2, "iso-carton"); }); - sc.tag(X(99), Y(16.5), z + 2.6, "Packaging", 5, "packaging"); - if (cfg.cassette) { - var cb = sc.sprite("cassette"); cb.box(X(93), Y(22.2), X(96.6), Y(23.2), z, z + 0.85, "iso-bench"); - cb.seq([0, 1, 2].map(function (k) { return [X(93.6 + k * 1.1), Y(22.7), function (q) { q.box(X(93.3 + k * 1.1), Y(22.4), X(94.0 + k * 1.1), Y(23.0), z + 0.85, z + 1.15, "iso-cass"); }]; })); - sc.inf("cassette", "Cassette duplication", "Bench line, ~40 to 80 m2 (project-timeline.md)"); - sc.tag(X(94.8), Y(22.7), z + 2.2, "Cassette", 3, "cassette"); - } - if (cfg.cdr) { - var cd = sc.sprite("cdr"); cd.box(X(98), Y(22.2), X(100.6), Y(23.2), z, z + 0.85, "iso-bench"); cd.box(X(98.4), Y(22.35), X(99.2), Y(23.05), z + 0.85, z + 1.6, "iso-cdtower"); cd.led(X(98.8), Y(22.7), z + 1.62, "iso-ledg"); - sc.inf("cdr", "CD-R duplication, short run", "Tower duplicator and printer bench, ~20 to 40 m2 (project-timeline.md)"); - sc.tag(X(99.3), Y(22.7), z + 2.2, "CD-R", 3, "cdr"); - } + // finishing and print block, ground floor (block floor plans): along the corridor trim + optical, QC lab, + // stair + goods lift; packaging behind with the cassette and CD-R benches; the upper floor is drawn at 1F + if (!up) finishingGF(sc, cfg, z); // warehouse: three racking rows; dispatch: staged pallets and a forklift [5, 11.5, 18].forEach(function (fy) { rackRow(sc, "warehouse", X(107.2), Y(fy), X(119.2), Y(fy + 1.2), z, 5.6, 4, 0, ["iso-pal-bx", "iso-pal-bx", "iso-pal-w", "", "iso-pal-bx"], "iso-rackf"); }); sc.tag(X(113), Y(12), z + 6.4, "Warehouse", 6, "warehouse"); @@ -1839,15 +1866,99 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } sc.tag(X(124), Y(10), z + 2.6, "Dispatch", 3, "dispatch"); sc.tag(X(66), Y(16), z + 3.8, "Press hall", 7, "presshall"); sc.tag(X(19.5), Y(14), z + 3.4, cfg.galvanics ? "Galvanics" : "Galvanics (empty)", 6, "galvanics"); - sc.tag(X(99), Y(4), z + 3.4, "Finishing", 5, "packaging"); } + // finishing and print block, ground floor (fx 92..106) + function finishingGF(sc, cfg, z) { + // trim and optical inspection (fx 92..96): the record path ends at the trimmer + var tr = sc.sprite("trim"); tr.box(X(93.0), Y(3.0), X(94.1), Y(4.1), z, z + 1.1, "iso-steel"); tr.disc(X(93.55), Y(3.55), z + 1.12, 0.26, "iso-vinylc"); + var op = sc.sprite("trim"); op.box(X(94.35), Y(2.95), X(95.75), Y(4.3), z, z + 1.5, "iso-optic"); op.cyl(X(95.05), Y(3.6), 0.35, z + 1.5, z + 1.8, "iso-dome"); op.led(X(95.05), Y(3.6), z + 1.82, "iso-ledg"); + sc.sprite("trim").box(X(94.6), Y(5.0), X(95.5), Y(5.8), z, z + 0.8, "iso-bin"); + sc.tag(X(94), Y(5), z + 2.6, "Trim + optical", 4, "trim"); + // QC lab (fx 96..100): bench on the packaging side + var qb = sc.sprite("qc"); qb.box(X(96.3), Y(6.7), X(99.7), Y(7.5), z, z + 0.85, "iso-bench"); + qb.seq([[X(97), Y(7.1), function (q) { q.box(X(96.5), Y(6.8), X(97.5), Y(7.4), z + 0.85, z + 1.0, "iso-instr"); q.disc(X(97), Y(7.1), z + 1.02, 0.24, "iso-vinylc"); }], + [X(98.3), Y(7.1), function (q) { q.box(X(97.95), Y(6.85), X(98.65), Y(7.35), z + 0.85, z + 1.45, "iso-instr2"); }], + [X(99.3), Y(7.1), function (q) { q.box(X(99.0), Y(6.9), X(99.55), Y(7.3), z + 0.85, z + 1.6, "iso-speaker"); }]]); + sc.tag(X(98), Y(5), z + 2.4, "QC + listening", 4, "qc"); + // stair and goods lift (fx 100..106), the same footprint on both levels + blockCore(sc, z, false); + sc.tag(X(103), Y(4.5), z + 3.0, "Stair + goods lift", 2, "block-stair"); + // packaging (~170 m2): packing tables, shrink wrap, pallet wrapper, cartons + [[93, 13.9, 96, 15.1], [93, 10.9, 96, 12.1], [97, 10.9, 100, 12.1]].forEach(function (t) { sc.sprite("packaging").box(X(t[0]), Y(t[1]), X(t[2]), Y(t[3]), z, z + 0.9, "iso-bench"); }); + var sh = sc.sprite("packaging"); sh.box(X(97), Y(14.8), X(99.8), Y(15.9), z, z + 1.3, "iso-shrink"); sh.open([X(97), Y(14.8)], [X(99.8), Y(14.8)], [[0.6, 2.2, z + 0.3, z + 1.1]], "iso-open"); + var pw = sc.sprite("packaging"); pw.cyl(X(102.75), Y(15.25), 1.0, z, z + 0.15, "iso-steel"); pw.box(X(102.55), Y(16.45), X(102.95), Y(16.75), z, z + 2.4, "iso-steel"); + pw.box(X(102.2), Y(14.7), X(103.3), Y(15.8), z + 0.15, z + 1.4, "iso-carton"); + [[104.9, 12.4], [104.9, 13.8], [103.5, 12.4]].forEach(function (p) { sc.sprite("packaging").box(X(p[0] - 0.6), Y(p[1] - 0.5), X(p[0] + 0.6), Y(p[1] + 0.5), z, z + 1.2, "iso-carton"); }); + sc.tag(X(98.5), Y(13), z + 2.6, "Packaging", 5, "packaging"); + sc.tag(X(99), Y(9.5), z + 3.6, "Finishing", 5, "packaging"); + if (cfg.cassette) { + var cb = sc.sprite("cassette"); cb.box(X(93), Y(18.7), X(96.6), Y(19.7), z, z + 0.85, "iso-bench"); + cb.seq([0, 1, 2].map(function (k) { return [X(93.6 + k * 1.1), Y(19.2), function (q) { q.box(X(93.3 + k * 1.1), Y(18.9), X(94.0 + k * 1.1), Y(19.5), z + 0.85, z + 1.15, "iso-cass"); }]; })); + sc.tag(X(94.8), Y(19.2), z + 2.2, "Cassette", 3, "cassette"); + } + if (cfg.cdr) { + var cd = sc.sprite("cdr"); cd.box(X(98), Y(18.7), X(100.6), Y(19.7), z, z + 0.85, "iso-bench"); cd.box(X(98.4), Y(18.85), X(99.2), Y(19.55), z + 0.85, z + 1.6, "iso-cdtower"); cd.led(X(98.8), Y(19.2), z + 1.62, "iso-ledg"); + sc.tag(X(99.3), Y(19.2), z + 2.2, "CD-R", 3, "cdr"); + } + } + // stair and goods lift of the block (fx 100..106, fy 2.6..7.9): a stair block on the ground floor, the stair well upstairs + function blockCore(sc, z, upper) { + var s = sc.sprite("block-stair"), st = [], k, a = X(100.4), b = X(101.5), c = Y(2.95), d = Y(7.5); + for (k = 1; k < 8; k++) st.push([a, c + k * 0.57, b, c + k * 0.57]); + if (!upper) { s.box(a, c, b, d, z, z + 1.6, "iso-stair"); s.lines(st, z + 1.61, "iso-steps"); } + else { s.flat(rect(a, c, b, d), z + 0.01, "iso-void", "block-stair"); s.lines(st, z + 0.02, "iso-steps"); s.box(b, c, b + 0.08, d, z, z + 1.0, "iso-rail2"); } + var lf = sc.sprite("block-stair"), L = sides(X(103.4), Y(2.95), X(105.6), Y(5.4)); + lf.box(X(103.4), Y(2.95), X(105.6), Y(5.4), z, z + 2.4, "iso-lift", { top: "iso-cut" }); + lf.open(L.N[0], L.N[1], [[0.5, 1.7, z, z + 2.1]], "iso-door").open(L.W[0], L.W[1], [[0.4, 1.6, z, z + 2.1]], "iso-door"); + } + // 1F: the finishing and print block's upper floor at +3.8 m on a closed lower storey + function blockUpper(sc, cfg) { + var P = sc.P, z = FFL + BLK.up, a = X(BLK.fx0), b = X(105.85), c = Y(0.3), d = Y(FW - 0.3), t = 0.25; + var s = sc.sprite("printblock"); + s.box(a, c, b, d, FFL, z, "iso-wall", { top: "iso-cut" }); + var rooms = [[92, 0.3, 100, 11.95, "iso-z-print", "printroom"], [92, 11.95, 100, 24.7, "iso-z-stock", "printstock"], + [100, 7.9, 105.85, 24.7, "iso-z-packmat", "packmat"], [100, 0.3, 105.85, 7.9, "iso-z-core", "block-stair"]], segs = []; + rooms.forEach(function (r) { + var p = X(r[0]), q = Y(r[1]), u = X(r[2]), v = Y(r[3]); + s.flat(rect(p, q, u, v), z + 0.005, r[4] + " iso-t", r[5]); + segs.push([p, q, u, q], [u, q, u, v], [u, v, p, v], [p, v, p, q]); + }); + s.lines(segs, z + 0.01, "iso-part"); + // walls cut above the slab; the east side is the fire wall + [[a, c, b, c + t, 0, -1, "iso-wall"], [a, d - t, b, d, 0, 1, "iso-wall"], [a, c + t, a + t, d - t, -1, 0, "iso-wall"], [b - t, c + t, b, d - t, 1, 0, "iso-firewall"]].forEach(function (w) { + sc.sprite("printblock").box(w[0], w[1], w[2], w[3], z, z + (P.vis(w[4], w[5]) ? 1.0 : 2.4), w[6], { top: "iso-cut" }); + }); + // print stock: paper and board racking; packaging materials: sleeves, inner bags, mailers, cartons + [14.0, 18.0, 22.0].forEach(function (fy) { rackRow(sc, "printstock", X(92.6), Y(fy), X(99.4), Y(fy + 1.0), z, 2.2, 3, 0, ["iso-pal-w", "iso-pal-bx", "iso-pal-w", ""], "iso-shelf"); }); + [10.0, 14.0, 18.0, 22.0].forEach(function (fy) { rackRow(sc, "packmat", X(100.6), Y(fy), X(105.2), Y(fy + 1.0), z, 2.2, 3, 0, ["iso-pal-bx", "iso-pal-w", "iso-pal-bx", ""], "iso-shelf"); }); + // print room in reserve: a free position, nothing installed + sc.sprite("printroom").flat(rect(X(93), Y(3.9), X(98), Y(6.9)), z + 0.02, "iso-slot iso-empty", "printroom"); + blockCore(sc, z, true); + sc.tag(X(95.5), Y(5.4), z + 1.6, "Print room (reserve)", 7, "printroom"); + sc.tag(X(96), Y(18), z + 3.0, "Print stock", 6, "printstock"); + sc.tag(X(103), Y(16), z + 3.0, "Packaging materials", 6, "packmat"); + sc.tag(X(103), Y(4.5), z + 3.0, "Stair + goods lift", 3, "block-stair"); + sc.tag(X(99), Y(12.5), z + 5.2, "Upper floor +3.8 m", 9, "printblock"); + } + // Pipe bridge over the 6 m lane: the deck runs between the factory's north wall (y 94) and the + // yard building (y 101); at both ends the pipes drop down the wall face to low-level wall sleeves, + // so the run never ends in the air (the pump/HX room is lower than the deck, and the GF cutaway + // trims the walls below it). User feedback, October 2026. function pipeBridge(sc, x) { - var s = sc.sprite("pipebridge"); + var s = sc.sprite("pipebridge"), Y0 = 94.3, Y1 = 100.7, parts = []; s.box(x - 0.15, 96.85, x + 0.15, 97.15, 0, 5.0, "iso-steel", { noTop: true }); - s.box(x - 0.9, 94, x + 0.9, 101, 5.0, 5.3, "iso-steel"); - s.seq([[x - 0.55, 97.5, function (q) { q.box(x - 0.7, 94, x - 0.4, 101, 5.3, 5.62, "iso-pipes"); }], - [x - 0.05, 97.5, function (q) { q.box(x - 0.2, 94, x + 0.1, 101, 5.3, 5.62, "iso-pipec"); }], - [x + 0.45, 97.5, function (q) { q.box(x + 0.3, 94, x + 0.6, 101, 5.3, 5.62, "iso-pipec2"); }]]); + s.box(x - 0.15, 99.35, x + 0.15, 99.65, 0, 5.0, "iso-steel", { noTop: true }); + s.box(x - 0.9, Y0, x + 0.9, Y1, 5.0, 5.3, "iso-steel"); + // wall sleeves where the pipes enter + s.box(x - 0.85, 93.9, x + 0.85, 94.1, 0.3, 0.9, "iso-steel"); + s.box(x - 0.85, 100.9, x + 0.85, 101.1, 0.3, 0.9, "iso-steel"); + [[-0.7, -0.4, "iso-pipes"], [-0.2, 0.1, "iso-pipec"], [0.3, 0.6, "iso-pipec2"]].forEach(function (pp) { + var a = x + pp[0], b = x + pp[1], c = pp[2]; + parts.push([(a + b) / 2, 97.5, function (q) { q.box(a, Y0, b, Y1, 5.3, 5.62, c); }]); + parts.push([(a + b) / 2, 94.2, function (q) { q.box(a, 94.0, b, Y0, 0.5, 5.62, c); }]); + parts.push([(a + b) / 2, 100.8, function (q) { q.box(a, Y1, b, 101.0, 0.5, 5.62, c); }]); + }); + s.seq(parts); } // ------------------------------------------------------------ utility yard @@ -2054,34 +2165,71 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } } // ------------------------------------------------------------ office building - function office(sc, cfg, inside) { - var o = OFC, P = sc.P, x0 = o.x0, y0 = o.y0, x1 = o.x1, y1 = o.y1; - sc.inf("office", "Office building, 32 x 15 m", "Two storeys (~960 m2): reception, canteen, changing rooms, mastering and cutting, listening, archive; offices, training and IT upstairs. Isolated from the factory floor, joined by a covered link (site-candidates.md)."); + // Rooms after the floor plans (cnx_vinyl_parametric_payap-office.png): [u0, v0, u1, v1, zone, id] in metres + // from the west end (u) and from the south facade (v). Stair and lift sit in the same place on both storeys. + var OFC_GF = [ + [0, 0, 6, 15, "iso-z-lobby", "office"], [6, 7.05, 32, 9.1, "iso-z-hall", "office"], + [6, 9.1, 10, 15, "iso-z-core", "office-stair"], [10, 9.1, 14.5, 15, "iso-z-staff", "changing"], [14.5, 9.1, 19, 15, "iso-z-staff2", "changing"], + [19, 9.1, 23, 15, "iso-z-arch", "archive"], [23, 9.1, 32, 15, "iso-z-cut", "cutting"], + [6, 0, 18, 7.05, "iso-z-canteen", "canteen"], [18, 0, 22, 7.05, "iso-z-kitchen", "canteen"], [22, 0, 26, 7.05, "iso-z-wc", "office-wc"], + [26, 0, 32, 7.05, "iso-z-listen", "listening"] + ]; + var OFC_1F = [ + [0, 9.1, 6, 15, "iso-z-meet", "meetingroom"], [6, 9.1, 10, 15, "iso-z-core", "office-stair"], [10, 9.1, 13.5, 15, "iso-z-it", "itroom"], + [13.5, 9.1, 16.6, 15, "iso-z-wc", "wc-1f"], [16.6, 9.1, 19, 15, "iso-z-pantry", "pantry"], [19, 9.1, 32, 15, "iso-z-flex", "flexroom"], + [0, 7.05, 32, 9.1, "iso-z-hall", "office"], [0, 0, 22, 7.05, "iso-z-offices", "offices"], [22, 0, 32, 7.05, "iso-z-meet2", "training"] + ]; + var OFC_UP = 4.0; // storey height: first floor at z0 + 4.0 (8 m to the roof for two storeys) + function officeInfo(sc, cfg) { + sc.inf("office", "Office building, 32 x 15 m", + "Two storeys (~960 m2) with a stair and a lift. Ground floor: reception and lobby, canteen, changing rooms, toilets, mastering and cutting, listening room, archive. " + + "First floor: offices, meeting and training, a meeting room, IT and server room, toilets, pantry, a flexible room. Isolated from the factory floor, joined by a covered link (site-candidates.md)."); sc.inf("cutting", cfg.cutting ? "Mastering and cutting room" : "Cutting room: cut abroad", cfg.cutting ? "Sillitoe SDMS lathe (USD 125k list) on the ground slab, away from press vibration and outside the crane zone; inner acoustic lining" : "Lacquers are cut by an outside studio (SST Frankfurt cuts Poy Records' lacquers today); the room stays for mastering"); sc.inf("lathe", "Sillitoe SDMS cutting lathe", "Integrated Space / Time Control automation, USD 125k published list price (vendor-landscape.md)"); - sc.inf("archive", "Archive: mobile shelving", "The father and the mother of every side; two fixed runs and four double-sided carriages, ~150 shelf metres, ~50,000 parts (facility-security.md, estimates)"); + sc.inf("archive", "Archive: mobile shelving", "The father and the mother of every side; two fixed runs and four double-sided carriages, ~150 shelf metres, ~50,000 parts, on the ground slab (facility-security.md, estimates)"); sc.inf("listening", "Listening room", "Start of the tour with the cutting-room window"); sc.inf("canteen", "Canteen and kitchen", "For about 40"); sc.inf("changing", "Changing rooms", "Women and men, four showers each; staff change here before crossing to the factory"); sc.inf("link", "Covered link", "Office to the start of the visitor corridor"); - // covered link (both modes) + sc.inf("office-stair", "Stair and lift", "Connect the two storeys of the office building (site-candidates.md)"); + sc.inf("office-wc", "Toilets", "Ground floor of the office building"); + sc.inf("offices", "Offices, ~150 m2", "First floor of the office building (site-candidates.md)"); + sc.inf("training", "Meeting and training room", "For the Payap programmes: mastering and lathe cutting, IT, business administration (payap-partnership.md); first floor"); + sc.inf("meetingroom", "Meeting room", "First floor of the office building"); + sc.inf("itroom", "IT and server room", "First floor of the office building"); + sc.inf("wc-1f", "Toilets", "First floor of the office building"); + sc.inf("pantry", "Pantry", "First floor of the office building"); + sc.inf("flexroom", "Flexible room", "A second classroom or more offices; first floor (site-candidates.md)"); + } + // facade openings: ground floor band always, the upper band only on the closed building (roof level) + function officeWindows(s, S, o, both) { + var gl = [], ul = [], wl = [], u, i; + for (u = 8.5, i = 0; u < 31; u += 3, i++) (hash(i + 3) > 0.35 ? gl : wl).push([u, u + 1.8, o.z0 + 0.9, o.z0 + 2.7]); + if (both) for (u = 1, i = 0; u < 31; u += 3, i++) (hash(i + 9) > 0.4 ? ul : wl).push([u, u + 1.8, o.z0 + 4.8, o.z0 + 6.8]); + s.open(S.S[0], S.S[1], wl, "iso-win").open(S.S[0], S.S[1], gl.concat(ul), "iso-win iso-lit").open(S.S[0], S.S[1], [[0.6, 5.6, o.z0, o.z0 + 3.2]], "iso-glass", "office"); + ["E", "W", "N"].forEach(function (k, j) { + var L = k === "N" ? 32 : 15, a = [], b = []; + for (u = 1.2, i = 0; u < L - 1.5; u += 3, i++) { + (hash(i + j * 17) > 0.45 ? a : b).push([u, u + 1.8, o.z0 + 0.9, o.z0 + 2.7]); + if (both) (hash(i + j * 29 + 5) > 0.45 ? a : b).push([u, u + 1.8, o.z0 + 4.8, o.z0 + 6.8]); + } + s.open(S[k][0], S[k][1], b, "iso-win").open(S[k][0], S[k][1], a, "iso-win iso-lit"); + }); + } + function office(sc, cfg, level) { + var o = OFC, x0 = o.x0, y0 = o.y0, x1 = o.x1, y1 = o.y1, S = sides(x0, y0, x1, y1); + officeInfo(sc, cfg); + // covered link (all levels) var lk = sc.sprite("link"); lk.seq([[15.7, 57.4], [18.3, 57.4], [15.7, 68.6], [18.3, 68.6]].map(function (p) { return [p[0], p[1], function (q) { q.box(p[0] - 0.1, p[1] - 0.1, p[0] + 0.1, p[1] + 0.1, 0, 3.5, "iso-steel", { noTop: true }); }]; })); lk.box(15.5, 57, 18.5, 69, 3.5, 3.75, "iso-roof3"); - if (!inside) { - var s = sc.sprite("office"), S = sides(x0, y0, x1, y1), gl = [], ul = [], wl = [], u, i; + if (level === "roof") { + var s = sc.sprite("office"); s.box(x0, y0, x1, y1, 0, o.z0, "iso-plinth"); s.box(x0, y0, x1, y1, o.z0, o.z1, "iso-office", { top: "iso-roof3" }); - for (u = 8.5, i = 0; u < 31; u += 3, i++) (hash(i + 3) > 0.35 ? gl : wl).push([u, u + 1.8, o.z0 + 0.9, o.z0 + 2.7]); - for (u = 1, i = 0; u < 31; u += 3, i++) (hash(i + 9) > 0.4 ? ul : wl).push([u, u + 1.8, o.z0 + 4.8, o.z0 + 6.8]); - s.open(S.S[0], S.S[1], wl, "iso-win").open(S.S[0], S.S[1], gl.concat(ul), "iso-win iso-lit").open(S.S[0], S.S[1], [[0.6, 7.2, o.z0, o.z0 + 3.2]], "iso-glass", "office"); - ["E", "W", "N"].forEach(function (k, j) { - var L = k === "N" ? 32 : 15, a = [], b = []; - for (u = 1.2, i = 0; u < L - 1.5; u += 3, i++) { (hash(i + j * 17) > 0.45 ? a : b).push([u, u + 1.8, o.z0 + 0.9, o.z0 + 2.7]); (hash(i + j * 29 + 5) > 0.45 ? a : b).push([u, u + 1.8, o.z0 + 4.8, o.z0 + 6.8]); } - s.open(S[k][0], S[k][1], b, "iso-win").open(S[k][0], S[k][1], a, "iso-win iso-lit"); - }); + officeWindows(s, S, o, true); s.flat(rect(x0 + 0.4, y0 + 0.4, x1 - 0.4, y1 - 0.4), o.z1 + 0.01, "iso-roofin"); s.seq([[x0 + 20.8, y0 + 5.6], [x0 + 23.4, y0 + 5.6], [x0 + 26, y0 + 9.4]].map(function (p) { return [p[0], p[1], function (q) { q.box(p[0] - 0.8, p[1] - 0.6, p[0] + 0.8, p[1] + 0.6, o.z1, o.z1 + 1.0, "iso-ac"); q.disc(p[0], p[1], o.z1 + 1.01, 0.42, "iso-fanring"); }]; @@ -2089,66 +2237,110 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } sc.tag((x0 + x1) / 2, (y0 + y1) / 2, o.z1 + 1.5, "Office", 9, "office"); return; } - var f = sc.sprite("office"), z = o.z0; - f.box(x0, y0, x1, y1, 0, z, "iso-plinth"); - var rooms = [[0, 0, 7, 15, "iso-z-lobby", "office"], [7, 8, 15, 15, cfg.cutting ? "iso-z-cut" : "iso-z-floor", "cutting"], [15, 8, 20, 15, "iso-z-listen", "listening"], - [20, 8, 24, 15, "iso-z-arch", "archive"], [24, 8, 32, 15, "iso-z-staff", "changing"], [7, 0, 25, 8, "iso-z-canteen", "canteen"], [25, 0, 32, 8, "iso-z-staff", "canteen"]]; - var segs = []; - rooms.forEach(function (r) { - f.flat(rect(x0 + r[0], y0 + r[1], x0 + r[2], y0 + r[3]), z + 0.005, r[4] + " iso-t", r[5]); - var a = x0 + r[0], b = y0 + r[1], c = x0 + r[2], d = y0 + r[3]; + var up = level === "1f", z = up ? o.z0 + OFC_UP : o.z0, f = sc.sprite("office"), segs = []; + f.box(x0, y0, x1, y1, 0, o.z0, "iso-plinth"); + if (up) { f.box(x0, y0, x1, y1, o.z0, z, "iso-office", { top: "iso-cut" }); officeWindows(f, S, o, false); } + (up ? OFC_1F : OFC_GF).forEach(function (r) { + var cls = r[5] === "cutting" && !cfg.cutting ? "iso-z-floor" : r[4], a = x0 + r[0], b = y0 + r[1], c = x0 + r[2], d = y0 + r[3]; + f.flat(rect(a, b, c, d), z + 0.005, cls + " iso-t", r[5]); segs.push([a, b, c, b], [c, b, c, d], [c, d, a, d], [a, d, a, b]); }); f.lines(segs, z + 0.01, "iso-part"); wallsCut(sc, "office", x0, y0, x1, y1, z, 1.0, 3.0, 0.25, "iso-office"); - // lobby: reception desk, stair - sc.sprite("office").box(x0 + 1, y0 + 3, x0 + 4, y0 + 3.8, z, z + 1.1, "iso-desk"); - var stair = sc.sprite("office"); stair.box(x0 + 4.4, y0 + 9.6, x0 + 6.6, y0 + 14.4, z, z + 1.6, "iso-stair"); - var steps = []; for (var si = 1; si < 8; si++) steps.push([x0 + 4.4, y0 + 9.6 + si * 0.6, x0 + 6.6, y0 + 9.6 + si * 0.6]); - stair.lines(steps, z + 1.61, "iso-steps"); - // cutting room + officeCore(sc, z, up); + if (up) officeUpper(sc, cfg, z); else officeGround(sc, cfg, z); + } + // stair (u 6.2..7.4) and lift (u 8.0..9.8) on the north side, off the corridor + function officeCore(sc, z, up) { + var x0 = OFC.x0, y0 = OFC.y0, s = sc.sprite("office-stair"), st = [], k, a = x0 + 6.2, b = x0 + 7.4, c = y0 + 9.3, d = y0 + 14.8; + for (k = 1; k < 9; k++) st.push([a, c + k * 0.61, b, c + k * 0.61]); + if (!up) { s.box(a, c, b, d, z, z + 1.6, "iso-stair"); s.lines(st, z + 1.61, "iso-steps"); } + else { s.flat(rect(a, c, b, d), z + 0.01, "iso-void", "office-stair"); s.lines(st, z + 0.02, "iso-steps"); s.box(b, c, b + 0.08, d, z, z + 1.0, "iso-rail2"); } + var lf = sc.sprite("office-stair"), L = sides(x0 + 8.0, y0 + 12.8, x0 + 9.8, y0 + 14.65); + lf.box(x0 + 8.0, y0 + 12.8, x0 + 9.8, y0 + 14.65, z, z + 2.4, "iso-lift", { top: "iso-cut" }); + lf.open(L.S[0], L.S[1], [[0.4, 1.4, z, z + 2.1]], "iso-door"); + } + function officeGround(sc, cfg, z) { + var x0 = OFC.x0, y0 = OFC.y0; + // lobby: reception desk near the entrance + sc.sprite("office").box(x0 + 1, y0 + 2.0, x0 + 3.2, y0 + 2.8, z, z + 1.1, "iso-desk"); + sc.tag(x0 + 3, y0 + 7, z + 2.6, "Reception", 3, "office"); + // mastering and cutting (north-east), inner acoustic lining + sc.sprite("cutting").flat(rect(x0 + 23.3, y0 + 9.4, x0 + 31.7, y0 + 14.7), z + 0.012, "iso-lining", "cutting"); if (cfg.cutting) { var lt = sc.sprite("lathe"); - lt.box(x0 + 9, y0 + 11, x0 + 11.2, y0 + 12.3, z, z + 1.0, "iso-lathe"); - lt.seq([[x0 + 9.7, y0 + 11.65, function (q) { q.disc(x0 + 9.7, y0 + 11.65, z + 1.02, 0.42, "iso-platter"); q.disc(x0 + 9.7, y0 + 11.65, z + 1.04, 0.36, "iso-lacquer"); }], - [x0 + 10.7, y0 + 11.65, function (q) { q.box(x0 + 10.45, y0 + 11.3, x0 + 10.95, y0 + 12.0, z + 1.0, z + 1.5, "iso-steel"); q.box(x0 + 10.0, y0 + 11.55, x0 + 10.6, y0 + 11.75, z + 1.4, z + 1.55, "iso-steel"); }]]); - lt.led(x0 + 11.2, y0 + 11.3, z + 0.8, "iso-ledo"); - var md = sc.sprite("cutting"); md.box(x0 + 12.4, y0 + 8.8, x0 + 14.6, y0 + 9.7, z, z + 0.75, "iso-desk"); - md.seq([[x0 + 13, y0 + 9.4, function (q) { q.box(x0 + 12.7, y0 + 9.3, x0 + 13.3, y0 + 9.45, z + 0.75, z + 1.2, "iso-screenm"); }], - [x0 + 14, y0 + 9.4, function (q) { q.box(x0 + 13.7, y0 + 9.3, x0 + 14.3, y0 + 9.45, z + 0.75, z + 1.2, "iso-screenm"); }]]); - sc.sprite("cutting").box(x0 + 7.6, y0 + 14.2, x0 + 8.2, y0 + 14.7, z, z + 1.1, "iso-speaker"); - sc.sprite("cutting").box(x0 + 13.8, y0 + 14.2, x0 + 14.4, y0 + 14.7, z, z + 1.1, "iso-speaker"); - sc.tag(x0 + 10.1, y0 + 11.6, z + 2.6, "Lathe", 7, "lathe"); + lt.box(x0 + 24.2, y0 + 10.4, x0 + 26.4, y0 + 11.7, z, z + 1.0, "iso-lathe"); + lt.seq([[x0 + 24.9, y0 + 11.05, function (q) { q.disc(x0 + 24.9, y0 + 11.05, z + 1.02, 0.42, "iso-platter"); q.disc(x0 + 24.9, y0 + 11.05, z + 1.04, 0.36, "iso-lacquer"); }], + [x0 + 25.9, y0 + 11.05, function (q) { q.box(x0 + 25.65, y0 + 10.7, x0 + 26.15, y0 + 11.4, z + 1.0, z + 1.5, "iso-steel"); q.box(x0 + 25.2, y0 + 10.95, x0 + 25.8, y0 + 11.15, z + 1.4, z + 1.55, "iso-steel"); }]]); + lt.led(x0 + 26.4, y0 + 10.7, z + 0.8, "iso-ledo"); + var md = sc.sprite("cutting"); md.box(x0 + 27.5, y0 + 12.8, x0 + 29.7, y0 + 13.7, z, z + 0.75, "iso-desk"); + md.seq([[x0 + 28.1, y0 + 13.5, function (q) { q.box(x0 + 27.8, y0 + 13.45, x0 + 28.4, y0 + 13.6, z + 0.75, z + 1.2, "iso-screenm"); }], + [x0 + 29.1, y0 + 13.5, function (q) { q.box(x0 + 28.8, y0 + 13.45, x0 + 29.4, y0 + 13.6, z + 0.75, z + 1.2, "iso-screenm"); }]]); + sc.sprite("cutting").box(x0 + 27.0, y0 + 14.0, x0 + 27.6, y0 + 14.5, z, z + 1.1, "iso-speaker"); + sc.sprite("cutting").box(x0 + 29.6, y0 + 14.0, x0 + 30.2, y0 + 14.5, z, z + 1.1, "iso-speaker"); + sc.tag(x0 + 25.3, y0 + 11, z + 2.6, "Lathe", 7, "lathe"); } else { - var sg = sc.sprite("cutting"); sg.box(x0 + 10.9, y0 + 11, x0 + 11.05, y0 + 12.6, z, z + 1.0, "iso-steel"); sg.box(x0 + 10.85, y0 + 10.9, x0 + 11.1, y0 + 12.7, z + 1.0, z + 2.0, "iso-sign"); - sc.tag(x0 + 11, y0 + 11.8, z + 2.6, "Cut abroad", 100, "cutting", "iso-signtag"); + var sg = sc.sprite("cutting"); sg.box(x0 + 25.9, y0 + 11, x0 + 26.05, y0 + 12.6, z, z + 1.0, "iso-steel"); sg.box(x0 + 25.85, y0 + 10.9, x0 + 26.1, y0 + 12.7, z + 1.0, z + 2.0, "iso-sign"); + sc.tag(x0 + 26, y0 + 11.8, z + 2.6, "Cut abroad", 100, "cutting", "iso-signtag"); } - sc.tag(x0 + 11, y0 + 9.5, z + 3.4, cfg.cutting ? "Cutting room" : "Cutting room (empty)", 6, "cutting"); - // listening room - sc.sprite("listening").box(x0 + 16, y0 + 9, x0 + 19, y0 + 9.9, z, z + 0.6, "iso-sofa"); - sc.sprite("listening").box(x0 + 15.6, y0 + 14, x0 + 16.2, y0 + 14.6, z, z + 1.2, "iso-speaker"); - sc.sprite("listening").box(x0 + 18.8, y0 + 14, x0 + 19.4, y0 + 14.6, z, z + 1.2, "iso-speaker"); + sc.tag(x0 + 28, y0 + 12.5, z + 3.4, cfg.cutting ? "Cutting room" : "Cutting room (empty)", 6, "cutting"); + // listening room (south-east): seats facing the speakers on the corridor wall + sc.sprite("listening").box(x0 + 28.0, y0 + 2.0, x0 + 30.5, y0 + 2.8, z, z + 0.6, "iso-sofa"); + sc.sprite("listening").box(x0 + 26.6, y0 + 6.2, x0 + 27.2, y0 + 6.7, z, z + 1.2, "iso-speaker"); + sc.sprite("listening").box(x0 + 30.8, y0 + 6.2, x0 + 31.4, y0 + 6.7, z, z + 1.2, "iso-speaker"); + sc.tag(x0 + 29, y0 + 3.5, z + 2.6, "Listening", 3, "listening"); // archive: two fixed runs, four double-sided carriages and one aisle (rails along the long side) - var ar = [[8.3, 8.7], [8.75, 9.55], [9.6, 10.4], [10.45, 11.25], [12.15, 12.95], [13.0, 13.4]]; + var ar = [[9.55, 9.95], [10.0, 10.8], [10.85, 11.65], [11.7, 12.5], [13.4, 14.2], [14.25, 14.65]]; sc.sprite("archive").seq(ar.map(function (r) { - return [x0 + 22, y0 + (r[0] + r[1]) / 2, function (q) { - var S = sides(x0 + 20.5, y0 + r[0], x0 + 23.5, y0 + r[1]), L = [], lv; - q.box(x0 + 20.5, y0 + r[0], x0 + 23.5, y0 + r[1], z, z + 2.2, "iso-archive"); + return [x0 + 21, y0 + (r[0] + r[1]) / 2, function (q) { + var S = sides(x0 + 19.5, y0 + r[0], x0 + 22.5, y0 + r[1]), L = [], lv; + q.box(x0 + 19.5, y0 + r[0], x0 + 22.5, y0 + r[1], z, z + 2.2, "iso-archive"); for (lv = 0; lv < 5; lv++) L.push([0.1, 2.9, z + 0.15 + lv * 0.42, z + 0.42 + lv * 0.42]); q.open(S.S[0], S.S[1], L, "iso-boxes").open(S.N[0], S.N[1], L, "iso-boxes"); }]; })); - sc.tag(x0 + 22, y0 + 11, z + 3.0, "Archive", 6, "archive"); - // canteen tables, kitchen counter, lockers - var tb = sc.sprite("canteen"); - var tl = []; for (var tx = 0; tx < 4; tx++) for (var ty = 0; ty < 2; ty++) tl.push([x0 + 9.5 + tx * 3.6, y0 + 2.4 + ty * 3.2]); - tb.seq(tl.map(function (p) { return [p[0], p[1], function (q) { q.box(p[0] - 0.9, p[1] - 0.45, p[0] + 0.9, p[1] + 0.45, z, z + 0.75, "iso-table"); }]; })); - sc.sprite("canteen").box(x0 + 25.6, y0 + 6.6, x0 + 31.4, y0 + 7.4, z, z + 0.9, "iso-bench"); - sc.sprite("changing").box(x0 + 24.6, y0 + 14.2, x0 + 31.4, y0 + 14.7, z, z + 1.9, "iso-locker"); - sc.sprite("changing").box(x0 + 24.6, y0 + 10.6, x0 + 29.4, y0 + 11.1, z, z + 1.9, "iso-locker"); - sc.tag(x0 + 3.5, y0 + 6, z + 2.6, "Reception", 3, "office"); - sc.tag(x0 + 16, y0 + 4, z + 2.2, "Canteen", 3, "canteen"); - sc.tag(x0 + 17.5, y0 + 11.5, z + 2.6, "Listening", 3, "listening"); + sc.tag(x0 + 21, y0 + 12, z + 3.0, "Archive", 6, "archive"); + // canteen tables (3 x 2), kitchen servery and line + var tl = []; for (var tx = 0; tx < 3; tx++) for (var ty = 0; ty < 2; ty++) tl.push([x0 + 8.2 + tx * 3.5, y0 + 1.7 + ty * 3.2]); + sc.sprite("canteen").seq(tl.map(function (p) { return [p[0], p[1], function (q) { q.box(p[0] - 1.2, p[1] - 0.5, p[0] + 1.2, p[1] + 0.5, z, z + 0.75, "iso-table"); }]; })); + sc.sprite("canteen").box(x0 + 18.2, y0 + 3.0, x0 + 19.0, y0 + 4.4, z, z + 0.9, "iso-bench"); + sc.sprite("canteen").box(x0 + 18.4, y0 + 0.5, x0 + 21.6, y0 + 1.3, z, z + 0.9, "iso-steel"); + sc.tag(x0 + 12, y0 + 3.5, z + 2.2, "Canteen", 3, "canteen"); + // changing rooms, women and men: showers on the north wall, lockers, a bench + [0, 4.5].forEach(function (dx) { + var cr = sc.sprite("changing"); + cr.box(x0 + 10.4 + dx, y0 + 13.7, x0 + 14.1 + dx, y0 + 14.6, z, z + 2.0, "iso-shower"); + cr.box(x0 + 13.5 + dx, y0 + 9.7, x0 + 14.1 + dx, y0 + 13.3, z, z + 1.9, "iso-locker"); + cr.box(x0 + 10.6 + dx, y0 + 11.5, x0 + 12.8 + dx, y0 + 11.9, z, z + 0.45, "iso-bench"); + }); + sc.tag(x0 + 14.5, y0 + 12, z + 2.8, "Changing", 3, "changing"); + // toilets + sc.sprite("office-wc").box(x0 + 22.4, y0 + 4.4, x0 + 25.6, y0 + 6.6, z, z + 2.0, "iso-wc"); + } + function officeUpper(sc, cfg, z) { + var o = OFC, x0 = o.x0, y0 = o.y0, list = [], r, c; + // offices (~150 m2): sixteen desks in two rows + for (r = 0; r < 2; r++) for (c = 0; c < 8; c++) list.push([x0 + 1.0 + c * 2.6, y0 + (r ? 4.4 : 1.2)]); + sc.sprite("offices").seq(list.map(function (p) { return [p[0] + 0.8, p[1] + 0.4, function (q) { q.box(p[0], p[1], p[0] + 1.6, p[1] + 0.8, z, z + 0.75, "iso-desk"); }]; })); + // meeting and training room (Payap programmes) + sc.sprite("training").seq([2.4, 3.7].map(function (v) { return [x0 + 27, y0 + v + 0.35, function (q) { q.box(x0 + 23.2, y0 + v, x0 + 30.8, y0 + v + 0.7, z, z + 0.75, "iso-table"); }]; })); + // meeting room, IT racks, toilets, pantry counter + sc.sprite("meetingroom").box(x0 + 1.0, y0 + 11.5, x0 + 5.0, y0 + 12.9, z, z + 0.75, "iso-table"); + var it = sc.sprite("itroom"), IS = sides(x0 + 10.6, y0 + 12.8, x0 + 12.4, y0 + 13.85); + it.box(x0 + 10.6, y0 + 12.8, x0 + 12.4, y0 + 13.85, z, z + 2.0, "iso-server"); + it.open(IS.S[0], IS.S[1], [[0.1, 0.55, z + 0.2, z + 1.85], [0.65, 1.15, z + 0.2, z + 1.85], [1.25, 1.7, z + 0.2, z + 1.85]], "iso-grille"); + it.led(x0 + 11.5, y0 + 12.8, z + 1.7, "iso-ledg"); + sc.sprite("wc-1f").box(x0 + 13.8, y0 + 12.5, x0 + 16.3, y0 + 14.6, z, z + 2.0, "iso-wc"); + sc.sprite("pantry").box(x0 + 16.8, y0 + 14.05, x0 + 18.7, y0 + 14.65, z, z + 0.9, "iso-bench"); + sc.tag((o.x0 + o.x1) / 2, y0 + 8, z + 4.4, "Office, first floor", 9, "office"); + sc.tag(x0 + 11, y0 + 4, z + 2.2, "Offices ~150 m2", 7, "offices"); + sc.tag(x0 + 27, y0 + 4.5, z + 2.2, "Meeting + training", 6, "training"); + sc.tag(x0 + 25.5, y0 + 12, z + 2.2, "Flexible room", 5, "flexroom"); + sc.tag(x0 + 3, y0 + 12, z + 2.2, "Meeting", 4, "meetingroom"); + sc.tag(x0 + 11.8, y0 + 12, z + 3.0, "IT + server", 4, "itroom"); + sc.tag(x0 + 17.8, y0 + 12, z + 2.2, "Pantry", 2, "pantry"); + sc.tag(x0 + 15, y0 + 11, z + 2.6, "WC", 1, "wc-1f"); + sc.tag(x0 + 8, y0 + 12, z + 3.0, "Stair + lift", 2, "office-stair"); } // ------------------------------------------------------------ phase 2 @@ -2242,16 +2434,25 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } } // ------------------------------------------------------------ scene - function buildScene(cfg, mode, rot, uid) { - var sc = new Scene(rot, uid), inside = mode === "inside", al = allocate(cfg), pv = pvPlace(cfg, al), P = sc.P; + // level: "roof" (closed buildings, PV), "gf" (ground-floor cutaway), "1f" (upper floors: the office first floor and + // the finishing and print block at +3.8 m; single-storey buildings stay in their ground-floor cutaway, ghosted) + function levelOf(m, d) { + m = String(m === undefined || m === null ? "" : m).toLowerCase(); + return m === "roof" ? "roof" : (m === "gf" || m === "inside" || m === "ground") ? "gf" : (m === "1f" || m === "upper") ? "1f" : d; + } + function buildScene(cfg, level, rot, uid) { + level = levelOf(level, "roof"); + var sc = new Scene(rot, uid), inside = level !== "roof", up = level === "1f", al = allocate(cfg), pv = pvPlace(cfg, al), P = sc.P; + var ctx = up ? Object.assign({}, cfg, { running: false }) : cfg; // context buildings stand still at 1F siteGround(sc, cfg, al); sitePieces(sc, cfg, al); parking(sc, cfg, pv); factoryInfo(sc, cfg, al); - if (inside) factoryInside(sc, cfg, al, pv); else factoryRoof(sc, cfg, pv); - office(sc, cfg, inside); + if (inside) { sc.dim = up; factoryInside(sc, ctx, al, pv, up); sc.dim = false; if (up) blockUpper(sc, cfg); } + else factoryRoof(sc, cfg, pv); + office(sc, cfg, level); var y = yard(sc, cfg, inside, pv); - if (al.p2show) phase2(sc, cfg, inside, al, pv); + if (al.p2show) { sc.dim = up; phase2(sc, ctx, inside, al, pv); sc.dim = false; } else { sc.inf("phase2", "Phase 2 reserve", "Ground kept for a second building: expansion beyond the current program, more pressing or finished goods (scale.md). Switch phase 2 on, or add presses beyond the ~870 m2 hall."); sc.tag((P2.x0 + P2.x1) / 2, (P2.y0 + P2.y1) / 2, 0.5, "Phase 2 reserve", 1, "phase2"); @@ -2316,16 +2517,18 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; } // ------------------------------------------------------------ mount var UID = 0; - var NOTE = "Schematic plant view after the Payap scenario model and the utility yard of scale.md; positions and envelopes are estimates. Drag to pan, pinch or Ctrl + wheel to zoom, tap an object for its figures."; + var NOTE = "Schematic plant view after the Payap scenario model and the utility yard of scale.md; positions and envelopes are estimates. GF, 1F and Roof switch between the ground floor, the upper floors and the roofs. Drag to pan, pinch or Ctrl + wheel to zoom, tap an object for its figures."; function mount(el, cfg, opts) { if (!el) throw new Error("CNXIso.mount: no container"); opts = opts || {}; - var st = { raw: Object.assign({}, cfg || {}), cfg: norm(cfg), mode: opts.mode === "inside" ? "inside" : "roof", rot: opts.rotation === undefined ? 3 : ((opts.rotation | 0) % 4 + 4) % 4, sel: null, uid: ++UID }; + var st = { raw: Object.assign({}, cfg || {}), cfg: norm(cfg), level: levelOf(opts.level || opts.mode, "roof"), rot: opts.rotation === undefined ? 3 : ((opts.rotation | 0) % 4 + 4) % 4, sel: null, uid: ++UID }; var wrap = document.createElement("div"); wrap.className = "iso-wrap"; wrap.innerHTML = '