Plant view: cars under the canopies, PV on the car park, yard lamps, silo order
Deploy test site / deploy (push) Successful in 47s

User feedback on the isometric view (October 2026):
- "the cars are above the car park roof": cars and scooters were their
  own sprites and the depth sort put some above the canopy; they are now
  drawn inside the canopy sprite before the posts and the roof, so the
  roof always covers them in every rotation.
- "a light pole in the middle of the yard": the two yard lamps at the
  service-lane edge (one in front of the boiler hall) move to the yard's
  back fence; one LAMPS list feeds both the poles and the light pools.
- "add solar panels to the car park": the car and scooter canopies now
  take PV first (107 modules, ~48 kWp; construction RFQ has canopy PV),
  the factory roof the rest (560 of 667 modules at 300 kWp); canopy
  tooltips give their kWp.
- "arrange the pvc storage outside to be black, red, yellow": the silos
  read black, black, red, yellow from the default north-east view.

Renderer checks rerun (288 + functional, no failures); calculator
regression 62 checks pass; 390 / 1024 / 1440 px, dark and light, no page
errors or overflow.
This commit is contained in:
grabowski committed 2026-10-16 18:10:53 +07:00
1 parent d7380919e4
commit 073f20a132
1 file changed
+20 -14
+20 -14
View File
@@ -1261,14 +1261,19 @@ 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]];
// ------------------------------------------------------------ PV placement
var PV_ROWS = [1, 3, 5, 7, 10, 12, 14, 16, 19, 21];
function pvFields(cfg, al) {
var f = [];
f.push({ key: "roofW", x0: X(1), cols: 103, y0: FY, rows: PV_ROWS, z: function (y) { return roofZ(y - FY) + 0.12; } });
f.push({ key: "roofE", x0: X(107.25), cols: 19, y0: FY, rows: PV_ROWS, z: function (y) { return roofZ(y - FY) + 0.12; } });
// the parking canopies carry PV first (construction RFQ: canopy PV), the factory roof takes the rest
f.push({ key: "cars", x0: 18.35, cols: 27, y0: 6, rows: [0.15, 2.45, 4.75], z: function (y) { return 2.9 + (y - 6) / 7 * 1.2 + 0.08; } });
f.push({ key: "scoot", x0: 20.35, cols: 13, y0: 26, rows: [0.4, 2.85], z: function (y) { return 2.45 + (y - 26) / 5.5 * 1.05 + 0.08; } });
f.push({ key: "roofW", x0: X(1), cols: 103, y0: FY, rows: PV_ROWS, z: function (y) { return roofZ(y - FY) + 0.12; } });
f.push({ key: "roofE", x0: X(107.25), cols: 19, y0: FY, rows: PV_ROWS, z: function (y) { return roofZ(y - FY) + 0.12; } });
if (al.p2show) f.push({ key: "p2", x0: P2.x0 + 1, cols: 40, y0: P2.y0, rows: [1, 3, 5, 7, 10, 12, 14, 16, 19, 21, 23],
z: function (y) { return FFL + P2.eave + (P2.high - P2.eave) * (y - P2.y0) / (P2.y1 - P2.y0) + 0.12; } });
return f;
@@ -1393,7 +1398,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
// buried conveying line from the silos to the colour store
g.push(linesS(P, [[43.5, 101.5, 43.5, 94]], 0.02, "iso-buried"));
// lamp light pools (visible at dusk)
[[116.5, 15], [116.5, 32], [132, 45], [146.6, 62], [101, 62], [56, 15], [13, 34], [60, 99.6], [110, 99.6], [8.5, 97]].forEach(function (p) {
LAMPS.forEach(function (p) {
var s = P.s(p[0], p[1], 0);
g.push('<ellipse class="iso-pool" cx="' + r1(s[0]) + '" cy="' + r1(s[1]) + '" rx="34" ry="17" fill="url(#iso-pool-' + sc.uid + ')"/>');
});
@@ -1449,7 +1454,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
ph.open(S2.S[0], S2.S[1], [[1, 2.2, 0, 2.2]], "iso-door").open(S2.E[0], S2.E[1], [[0.6, 2.4, 1.6, 2.4]], "iso-louvre");
sc.inf("firewater", "Fire water tank and pump house", "9 m x 4 m tank, ~200 m3 usable (planning figure); pumps: duty electric, standby diesel, jockey (facility-security.md)");
// lamps
[[116.5, 15], [116.5, 32], [132, 45], [146.6, 62], [101, 62], [56, 15], [13, 34], [60, 99.6], [110, 99.6], [8.5, 97]].forEach(function (p) { lamp(sc, p[0], p[1]); });
LAMPS.forEach(function (p) { lamp(sc, p[0], p[1]); });
// trees
var T = [], x, y;
for (y = 8; y <= 112; y += 13) T.push([2.6, y, 1.6 + hash(y) * 0.6]);
@@ -1464,25 +1469,25 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
sc.inf("site", "Site plot (schematic)", "Payap scenario layout on a placeholder plot; road assumed south (site-candidates.md)");
}
function parking(sc, cfg, pv) {
// cars under the canopy (static), then the canopies with or without PV
// cars and scooters are drawn inside their canopy's sprite, before the posts and the roof,
// so the roof always covers them (as separate sprites the depth sort put some above the roof)
var cols = ["iso-car1", "iso-car2", "iso-car3", "iso-car4"];
[0, 1, 3, 4, 6, 8, 9].forEach(function (b, i) {
var x = 18 + 2.77 * b + 0.5, s = sc.sprite("parking");
s.box(x, 6.6, x + 1.75, 11.2, 0.25, 1.05, cols[i % 4]).box(x + 0.15, 7.8, x + 1.6, 10.2, 1.05, 1.55, "iso-glassd");
});
var sco = sc.sprite("parking");
for (var k = 0; k < 7; k++) sco.box(20.4 + 1.37 * k * 1.4, 26.9, 20.8 + 1.37 * k * 1.4, 28.1, 0.2, 1.0, cols[(k + 1) % 4]);
var cp = sc.sprite("canopy-cars");
[0, 1, 3, 4, 6, 8, 9].forEach(function (b, i) {
var x = 18 + 2.77 * b + 0.5;
cp.box(x, 6.6, x + 1.75, 11.2, 0.25, 1.05, cols[i % 4]).box(x + 0.15, 7.8, x + 1.6, 10.2, 1.05, 1.55, "iso-glassd");
});
[18.1, 31.85, 45.6].forEach(function (x) { cp.box(x - 0.13, 9.37, x + 0.13, 9.63, 0, 3.4, "iso-steel", { noTop: true }); });
cp.prism(rect(18, 6, 45.7, 13), [2.75, 2.75, 3.95, 3.95], [2.9, 2.9, 4.1, 4.1], "iso-canopy");
pvDraw(cp, pv.by.cars.F, pv.by.cars.panels);
var sp = sc.sprite("canopy-scooters");
for (var k = 0; k < 7; k++) sp.box(20.4 + 1.37 * k * 1.4, 26.9, 20.8 + 1.37 * k * 1.4, 28.1, 0.2, 1.0, cols[(k + 1) % 4]);
[20.1, 33.6].forEach(function (x) { sp.box(x - 0.12, 28.6, x + 0.12, 28.85, 0, 2.9, "iso-steel", { noTop: true }); });
sp.prism(rect(20, 26, 33.7, 31.5), [2.3, 2.3, 3.35, 3.35], [2.45, 2.45, 3.5, 3.5], "iso-canopy");
pvDraw(sp, pv.by.scoot.F, pv.by.scoot.panels);
var nc = pv.by.cars.panels.length, ns = pv.by.scoot.panels.length;
sc.inf("canopy-cars", "Car canopy, 10 bays", "27.7 x 7.0 m at 4.1 m (construction RFQ)" + (nc ? "; " + nc + " PV modules (roof full)" : ""));
sc.inf("canopy-scooters", "Scooter canopy, ~30 spaces", "13.7 x 5.5 m at 3.5 m (construction RFQ)" + (ns ? "; " + ns + " PV modules (roof full)" : ""));
sc.inf("canopy-cars", "Car canopy, 10 bays", "27.7 x 7.0 m at 4.1 m (construction RFQ)" + (nc ? "; " + nc + " PV modules (~" + fmt(nc * 0.45) + " kWp)" : ""));
sc.inf("canopy-scooters", "Scooter canopy, ~30 spaces", "13.7 x 5.5 m at 3.5 m (construction RFQ)" + (ns ? "; " + ns + " PV modules (~" + fmt(ns * 0.45) + " kWp)" : ""));
}
// ------------------------------------------------------------ factory
@@ -2031,7 +2036,8 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
sc.tag(27, 106, inside ? 5.2 : WH + 1, "Water treatment", 7, "wt");
}
// PVC silos and big-bag station
var cols = ["iso-silok", "iso-silok", "iso-siloy", "iso-silor"];
// x ascending; from the default view (north-east) they read black, black, red, yellow (user, October 2026)
var cols = ["iso-siloy", "iso-silor", "iso-silok", "iso-silok"];
[37.5, 41.5, 45.5, 49.5].forEach(function (x, k) {
var s = sc.sprite("silos");
s.cyl(x, 103.6, 0.9, 0, 2.2, "iso-steel", { noTop: true });