Resources Numerical accuracy Calculation policy

Material flow drawing and playback

Draws the layout in the 3D view and moves the loads and vehicles of a finished or running simulation.

How the precision is kept

The playback reads the run's recorded trace and works out where each load and vehicle is at a time, in 64-bit numbers; the view turns those into boxes for the graphics card. Nothing here feeds the reported statistics.

Calculations under this policy

4 calculations in 2 source files. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.

addons/flow/lib/playback.js ModelCraft 0.8.1 32-bit, display only 2 calculations Reviewed

Tests None found by the review

Where a load is drawn on a belt

32-bit, display only

The distance along a belt at which a load is drawn at a given time.

accumulating: s = min(v × t + c, S); stopping belt: s = D(t) − D at entry; drawn at max(0, s) + L ÷ 2

Uses the regimes the simulation recorded (c and S per load, or the belt's distance history found by binary search). The result only places a box in the view.

  • addons/flow/lib/playback.js
  • lines 71 to 81
  • ModelCraft 0.8.1
else if (sp && sp.type === 'conveyor' && g && g.path) {
  let s;
  if (r.k) {
    let k = r.k[0];
    for (const x of r.k) { if (x[0] <= t) k = x; else break; }
    s = Math.min(sp.speed * t + k[1], k[2]);
  } else {
    const D = beltD(r.el, t);
    s = D === null ? 0 : D - r.De;
  }
  at = along(r.el, Math.max(0, s) + r.L / 2);

Where a vehicle is drawn

32-bit, display only

A vehicle's point on its path segment at a given time.

f = (t − t₀) ÷ (t₁ − t₀); point = path at f × length

Linear in time between the recorded start and end of the segment. The result only places a box in the view.

  • addons/flow/lib/playback.js
  • lines 51 to 60
  • ModelCraft 0.8.1
function vehicleAt(n, t) {
  const ms = moves.get(n) || [];
  let m = ms[0];
  for (const x of ms) { if (x.t0 <= t) m = x; else break; }
  if (!m) return null;
  const g = geom[m.e];
  if (!g || !g.path) return null;
  const f = m.t0 < 0 || t >= m.t1 ? 1 : (t - m.t0) / (m.t1 - m.t0);
  return g.path.at(f * g.path.length);
}
addons/flow/view.js ModelCraft 0.8.1 32-bit, display only 2 calculations Reviewed

Tests None found by the review

Belt strips

32-bit, display only

The boxes drawn for a conveyor between points sampled along its path.

pieces = max(4, ceil(|angle| ÷ 10)) for a curve, else 1; length = √(run² + Δz²); turn = atan2(Δy, Δx)

Each piece is a box from one sampled point to the next, lengthened by 0.02 m on curves so the pieces overlap. Drawing only.

  • addons/flow/view.js
  • lines 60 to 67
  • ModelCraft 0.8.1
if (CONVEYOR_KINDS.includes(it.kind) && g.path) {
  const n = it.kind === 'curve' ? Math.max(4, Math.ceil(Math.abs(it.params.angle) / 10)) : 1;
  const w = it.params.width;
  for (let i = 0; i < n; i++) {
    const a = g.path.at((g.path.length * i) / n), b = g.path.at((g.path.length * (i + 1)) / n);
    const dx = b.p[0] - a.p[0], dy = b.p[1] - a.p[1], dz = b.p[2] - a.p[2], run = Math.hypot(dx, dy), len = Math.hypot(run, dz);
    this.box((a.p[0] + b.p[0]) / 2, (a.p[1] + b.p[1]) / 2, (a.p[2] + b.p[2]) / 2 - 0.03, len + (n > 1 ? 0.02 : 0), w, 0.06, (Math.atan2(dy, dx) * 180) / Math.PI, sel ? COL.beltSel : COL.belt, Math.atan2(dz, run));
  }

Loads and vehicles as instanced boxes

32-bit, display only

Sets the position, turn and size of each load and vehicle box for one frame.

Positions and headings from the playback frame are written into three.js instance matrices for the graphics card.

  • addons/flow/view.js
  • lines 142 to 165
  • ModelCraft 0.8.1
drawFrame(f) {
  const loads = f ? f.loads : [], vehicles = f ? f.vehicles : [];
  const o = new THREE.Object3D(), c = new THREE.Color();
  const lm = this.ensure('load', loads.length);
  loads.forEach((l, i) => {
    o.position.set(l.p[0], l.p[1], l.p[2] + 0.13);
    o.rotation.set(0, 0, (l.h * Math.PI) / 180);
    o.scale.set(Math.max(0.05, l.L - 0.02), 0.34, 0.24);
    o.updateMatrix(); lm.setMatrixAt(i, o.matrix);
    if (!this.typeIdx.has(l.type)) this.typeIdx.set(l.type, this.typeIdx.size);
    lm.setColorAt(i, c.setHex(COL.loads[this.typeIdx.get(l.type) % COL.loads.length]));
  });
  lm.count = loads.length; lm.instanceMatrix.needsUpdate = true; if (lm.instanceColor) lm.instanceColor.needsUpdate = true;
  const vm = this.ensure('veh', vehicles.length);
  vehicles.forEach((v, i) => {
    o.position.set(v.p[0], v.p[1], v.p[2] + 0.1);
    o.rotation.set(0, 0, (v.h * Math.PI) / 180);
    o.scale.set(1.0, 0.7, 0.2);
    o.updateMatrix(); vm.setMatrixAt(i, o.matrix);
    vm.setColorAt(i, c.setHex(COL.vehicle));
  });
  vm.count = vehicles.length; vm.instanceMatrix.needsUpdate = true; if (vm.instanceColor) vm.instanceColor.needsUpdate = true;
  this.view.invalidate();
}

In detail

Method
Belt strips are boxes between sampled path points (a curve is cut into pieces of at most 10°); loads sit at tail + length ÷ 2 along the belt's center line.
Number types
64-bit numbers in playback; three.js matrices for drawing.
Limits
A curved belt is drawn as straight pieces. Drawn positions are for looking at, not for measuring.

Approach: Display only

Computed to draw the screen, often in 32-bit on the graphics processor. No number a person reads, saves or exports comes from it.

Every policy that takes this approach