Data centres: multi-storey shell, racks and pods, cooling, fire safety, power
Deploy web Studio / deploy (push) Successful in 1m4s

datacentre.rhai (palette groups Data centre, Cooling, Fire safety, Power):

- dc_building: multi-storey shell (storeys of storey_h), walls in pieces,
  slabs, roof with parapet, louvres, front door and loading dock; levels
  cut it open storey by storey
- raised_floor; rack_row and dc_pod (cabinets by U, width, depth,
  equipment, fill, doors, LEDs; in-row coolers; cold / hot aisle
  containment with vent tiles, roof, end doors, ceiling duct, airflow);
  dc_cage for colocation
- crah, chiller (air-cooled, dry, adiabatic), cooling_tower, pipe_run
- smoke_detectors (grid + coverage), fire_pipe (VESDA, sprinkler, gas),
  gas_bank (Novec 1230, FM-200, Inergen sized for its halls), fire_device
  (panels, call points, abort switches, beacons, extinguishers)
- power_cab (UPS, battery, PDU, switchgear, RPP), busway, cable_tray,
  transformer
- tooltips sum IT load, kW/m2, cooling redundancy per storey, UPS load,
  battery runtime, plant cooling and agent quantity (warning when short)

Engine and stdlib:

- part_hidden(o) in house.rhai: parts on closed storeys inside a
  dc_building are not drawn (they only made sort cycles under the slab
  above); house parts, furniture and people use it. Existing examples
  render byte-identical.
- overhead services sort in their own "ceiling" layer with tight boxes
- sort records which sprites each broken cycle involved; isokit check
  names them (Rendered.cycle_at)

examples/datacentre (three storeys and a roof plant), Data centre demo in
the web Studio, tests, SCRIPTING/README/ROADMAP, MCP instructions.
This commit is contained in:
grabowski committed 2026-10-27 17:08:39 +07:00
1 parent c8becc2e59
commit 2810477f1b
22 files changed
+2281 -18

No files matched your search

+10
View File
@@ -59,6 +59,10 @@ pub struct Rendered {
pub nodes: usize,
/// Depth-sort cycles that had to be broken (0 is ideal).
pub cycles: usize,
/// Where they were broken: "a before b, c" (sprite ids; a was drawn
/// before b and c, which it should have followed).
#[serde(skip_serializing_if = "Vec::is_empty")]
pub cycle_at: Vec<String>,
pub warnings: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub order: Vec<OrderItem>,
@@ -714,6 +718,7 @@ pub fn render(scene: &Scene, rot: u8, opts: &RenderOpts) -> Rendered {
let mut frame = [1e9f64, 1e9, -1e9, -1e9];
let mut sprites = 0;
let mut cycles = 0;
let mut cycle_at = vec![];
let mut order = vec![];
let mut warnings = scene.warnings.clone();
@@ -748,6 +753,10 @@ pub fn render(scene: &Scene, rot: u8, opts: &RenderOpts) -> Rendered {
.collect();
let o = sort::sort_hulls(&pairs, &hulls, layer.opts.zfirst);
cycles += o.cycles;
for (b, before) in &o.broken {
let ids: Vec<&str> = before.iter().map(|&i| live[i].id.as_str()).collect();
cycle_at.push(format!("{} before {}", live[*b].id, ids.join(", ")));
}
o.order
} else {
(0..live.len()).collect()
@@ -876,6 +885,7 @@ pub fn render(scene: &Scene, rot: u8, opts: &RenderOpts) -> Rendered {
sprites,
nodes,
cycles,
cycle_at,
warnings,
order,
html,
+28
View File
@@ -153,6 +153,9 @@ fn rect_of(b: &Bb) -> Hull {
pub struct Order {
pub order: Vec<usize>,
pub cycles: usize,
/// Each broken cycle: the sprite drawn early to break it and the sprites
/// not drawn yet that should have come before it.
pub broken: Vec<(usize, Vec<usize>)>,
}
pub fn sort(boxes: &[(Bb, Sb)], zf: bool) -> Order {
@@ -203,6 +206,7 @@ pub fn sort_hulls(boxes: &[(Bb, Sb)], hulls: &[Option<Hull>], zf: bool) -> Order
let mut out = Order {
order: Vec::with_capacity(n),
cycles: 0,
broken: vec![],
};
for _ in 0..n {
let mut best: Option<usize> = None;
@@ -227,6 +231,12 @@ pub fn sort_hulls(boxes: &[(Bb, Sb)], hulls: &[Option<Hull>], zf: bool) -> Order
}
}
let b = best.expect("n > 0");
if indeg[b] > 0 {
let before: Vec<usize> = (0..n)
.filter(|&i| !done[i] && adj[i].contains(&b))
.collect();
out.broken.push((b, before));
}
done[b] = true;
out.order.push(b);
for &j in &adj[b] {
@@ -245,6 +255,24 @@ mod tests {
([x0, y0, x1, y1, z0, z1], [0.0, 0.0, 10.0, 10.0])
}
#[test]
fn broken_cycles_name_the_sprites() {
// a before b (x), b before c (y), c before a (z): no order fits all three
let o = sort(
&[
bx(0.0, 0.0, 1.0, 10.0, 5.0, 6.0),
bx(2.0, 0.0, 3.0, 1.0, 0.0, 5.0),
bx(0.0, 2.0, 3.0, 3.0, 0.0, 1.0),
],
false,
);
assert_eq!(o.cycles, 1);
assert_eq!(o.broken.len(), 1);
let (b, before) = &o.broken[0];
assert_eq!(before.len(), 1);
assert_ne!(*b, before[0]);
}
#[test]
fn separated_on_x_draws_far_first() {
let o = sort(