Factorio Fluid Model Proposal

A model of a straight pipe using the wave equation. Source and sink can be turned on and off. Fluid output and intake is measured.

by TheYeast

HTML

<div id="main">
  <input type="checkbox" id="cbSource" checked="checked"/>source
  <div id="pipe">
  </div>
  <input type="checkbox" id="cbSink" checked="checked"/>sink
  <div>total fluid: <span id="vTotal">s</span></div>
  <div>source output: <span id="vSource">s</span></div>
  <div>sink intake: <span id="vSink">s</span></div>
</div>

CSS

body {
  background: #20262E;
  padding: 20px;
  color: #eee;
}

.seg {
  position: relative;
  width: 20px;
  height: 20px;
}

.fluid {
  position: relative;  
  width: 20px;
  height: 20px;
}

.flow {
  position: absolute;
  background: #f7f;  
  left: 30px;
  top: 5px;
  height: 5px;

JavaScript

var ups = 60; // updates per second
var n = 10 + 2; // 10 pipe + source & sink
var sourceOutMax = 6000; // source supply limit per second
var sinkInMax = 6000; // sink intake limit per second
var cSquared = 0.01;  // see equations and blog posts
var friction = 0.001; // see equations and blog posts

var pipe = [];
for (var i = 0; i < n; ++i) {
	var div = $("<div class='seg'></div>");
  var fluid = $("<div class='fluid'></div>");
  var flow = $("<div class='flow'></div>");
  $("#pipe").append(div);
	div.append(fluid);
  div.append(flow);
	pipe[i] = {
  	level : 0,
    flowBot : 0,
    div : div,
    divFluid : fluid,
    divFlow : flow
  };
}

window.setInterval(function() { update() }, 1000/ups);

function update() {
	
  var src = updateSource(pipe[0]);
	var sunk = updateSink(pipe[n-1]);
  var i;
	for (i = 0; i < n-1; ++i) {
    updateFlow(pipe[i], pipe[i+1]);
	}
	for (i = 0; i < n-1; ++i) {
    updateLevel(pipe[i], pipe[i+1]);
	}
  var t = 0;
	for (i = 0; i < n; ++i) {
    draw(pipe[i]);
    t += pipe[i].level;
	}
	$("#vTotal").text(Math.round(t));
	$("#vSource").text(Math.round(src*ups) + "/s");
	$("#vSink").text(Math.round(sunk*ups) + "/s");
}

function updateFlow(p0, p1) {
	p0.flowBot += (p0.level-p1.level) * cSquared;
	p0.flowBot *= (1 - friction);
  if (p0.flowBot > 0 && p0.flowBot > 0.25 * p0.level) 
  	p0.flowBot = 0.25 * p0.level; 
  if (p0.flowBot < 0 && -p0.flowBot > 0.25 * p1.level) 
  	p0.flowBot = -0.25 * p1.level; 
}

function updateLevel(p0, p1) {
	p0.level -= p0.flowBot;
  p1.level += p0.flowBot;
}

function draw(p) {
  var opc = Math.min(Math.abs(p.level) * 0.005, 1);
	p.divFluid.css("opacity", opc);
	p.divFluid.css("background", p.level > 0 ? "#07f" : "#f70");
  var f = Math.abs(p.flowBot) * 0.5;
  p.divFlow.css("width", f + "px");
  p.divFlow.css("left", p.flowBot > 0 ? 50 : (50-f) + "px")
	p.divFlow.css("background", p.flowBot > 0 ? "#7f0" :"#f7f");
}

function updateSource(p) {
	var src = 0;
  if ($("#cbSource").prop("checked")) {
    src =...