Functional Weave
Code in Python

logistics.container-fit@1.0.0

impl/typescript.ts

3,379 bytes · the TypeScript implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

import { CONTAINERS } from "./logistics_container_fit_data.ts";  ← this capability’s own data, compiled from data/containers.json into the same file by fune build
import { type ContainerFit } from "./logistics_container_fit_types.ts";

/**
 * How many identical cartons fit in a container or trailer: the best block
 * stow over the allowed orientations that pass the door, capped by payload.
 */
export function containerFit(
  container: string,
  lengthMm: number,
  widthMm: number,
  heightMm: number,
  cartonGrams: number,
  keepUpright: boolean,
  maxPayloadKg: number | null,
): ContainerFit {
  for (const d of [lengthMm, widthMm, heightMm]) {
    if (!Number.isInteger(d) || d < 1) throw new RangeError(`carton dimensions must be 1 mm or more, received ${d}`);
  }
  if (!Number.isInteger(cartonGrams) || cartonGrams < 1) {
    throw new RangeError(`cartonGrams must be 1 or more, received ${cartonGrams}`);
  }
  const spec = CONTAINERS.find((c) => c.code === container);
  if (spec === undefined) {
    throw new RangeError(`unknown container "${container}": expected ${CONTAINERS.map((c) => c.code).join(", ")}`);
  }
  let payloadKg = spec.maxPayloadKg;
  if (maxPayloadKg !== null) {
    if (!Number.isInteger(maxPayloadKg) || maxPayloadKg < 1) {
      throw new RangeError(`maxPayloadKg must be 1 or more, received ${maxPayloadKg}`);
    }
    if (maxPayloadKg > spec.maxPayloadKg) {
      throw new RangeError(`maxPayloadKg must not exceed ${spec.maxPayloadKg} kg for ${container}, received ${maxPayloadKg}`);
    }
    payloadKg = maxPayloadKg;
  }

  const l = lengthMm;
  const w = widthMm;
  const h = heightMm;
  // Upright first (as given, then turned on the floor), then the four tipped ones.
  const orientations: ReadonlyArray<readonly [number, number, number]> = keepUpright
    ? [[l, w, h], [w, l, h]]
    : [[l, w, h], [w, l, h], [l, h, w], [h, l, w], [w, h, l], [h, w, l]];

  let best: { along: number; across: number; layers: number; placed: readonly [number, number, number] } | null = null;
  let bestCount = 0;
  for (const o of orientations) {
    const [a, b, c] = o;
    if (a > spec.lengthMm || b > spec.widthMm || c > spec.heightMm) continue;
    // End-loaded containers: the carton's cross-section has to pass the door.
    if (spec.doorWidthMm !== null && b > spec.doorWidthMm) continue;
    if (spec.doorHeightMm !== null && c > spec.doorHeightMm) continue;
    const along = Math.floor(spec.lengthMm / a);
    const across = Math.floor(spec.widthMm / b);
    const layers = Math.floor(spec.heightMm / c);
    const count = along * across * layers;
    if (best === null || count > bestCount) {
      best = { along, across, layers, placed: o };
      bestCount = count;
    }
  }
  if (best === null) throw new RangeError(`carton does not fit in ${container} in any allowed orientation`);

  const weightLimit = Math.floor((payloadKg * 1000) / cartonGrams);
  const cartons = Math.min(bestCount, weightLimit);
  const space = spec.lengthMm * spec.widthMm * spec.heightMm;
  return {
    container,
    cartons,
    spaceLimit: bestCount,
    weightLimit,
    limitedBy: weightLimit < bestCount ? "weight" : "space",
    alongLength: best.along,
    acrossWidth: best.across,
    layers: best.layers,
    placedLengthMm: best.placed[0],
    placedWidthMm: best.placed[1],
    placedHeightMm: best.placed[2],
    cargoGrams: cartons * cartonGrams,
    volumeUsedBasisPoints: Math.floor((cartons * l * w * h * 10000) / space),
  };
}