Last active
December 21, 2015 05:49
-
-
Save rctay/6260157 to your computer and use it in GitHub Desktop.
math for physics/physical quantities (viz. values with units), in "worksheets" http://plnkr.co/edit/CHccBbCGltDI2e3U1kVY?p=preview
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Code goes here | |
angular.module('aModule', []) | |
.service('quant_alg', function() { | |
return quant_alg; | |
}) | |
.controller('ControllerA', function($scope, quant_alg) { | |
var qa = quant_alg; | |
angular.extend($scope, qa); | |
}) | |
.directive('value', function(quant_alg) { | |
return { | |
restrict: 'E', | |
replace: true, | |
scope: { | |
readOnly: '@', | |
property: '=', | |
value: '&', | |
unitsExpr: '&' | |
}, | |
link: function(scope, iElement, iAttrs, controller) { | |
if (iAttrs.readOnly === "") | |
return; | |
var units; | |
if (iAttrs.unitsExpr) | |
units = scope.unitsExpr(); | |
else { | |
units = iAttrs.units; | |
} | |
scope.property = quant_alg.value(iAttrs.display || iAttrs.property, scope.value(), units); | |
}, | |
template: '<div class="value">' | |
+ '<div class="vvalue" ng-switch on="readOnly">' | |
+ '<span ng-switch-when="" ng-bind="property.value"></span>' | |
+ '<input ng-switch-default type="text" ng-model="property.value">' | |
+ '</div>' | |
+ '<span class="units">{{ property.units.toString() }}</span>' | |
+ '</div>' | |
}; | |
}) | |
.directive('comp', function() { | |
return { | |
restrict: 'E', | |
replace: true, | |
scope: { | |
expr: '&', | |
property: '=set' | |
}, | |
link: function(scope, iElement, iAttrs, controller) { | |
var shallSet = !!iAttrs.set; | |
scope.$watch("expr()", function(ret, old) { | |
if (!ret.length) | |
return; | |
scope.lines = ret[0]; | |
if (shallSet) { | |
var v = ret[1]; | |
v.name = iAttrs.display || iAttrs.set; | |
scope.property = v; | |
} | |
}, true); | |
}, | |
template: '<div">' | |
+ '<span ng-repeat="line in lines">' | |
+ '{{line}}' | |
+ '<br ng-if="!last"/>' | |
+ '</span>' | |
+ '</div>' | |
}; | |
}); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html ng-app="aModule"> | |
<head> | |
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.js" | |
data-require="angular.js@*" | |
data-semver="1.1.5"></script> | |
<script src="//cdn.jsdelivr.net/underscorejs/1.5.1/underscore-min.js" | |
data-require="underscore.js@*" | |
data-semver="1.5.1"></script> | |
<link rel="stylesheet" href="style.css" /> | |
<script src="quant_alg.js"></script> | |
<script src="app.js"></script> | |
</head> | |
<body> | |
<div ng-controller="ControllerA"> | |
<p>rain falls at a constant rate of <value | |
property="rain_rate" display="rain rate" | |
value="5" | |
units-expr="units('drops').over('m', 2).over('s')"></value> | |
</p> | |
<p>windscreen has width <value | |
property="width" | |
value="1" | |
units="m"> | |
</value> by breadth <value | |
property="breadth" | |
value="1" | |
units="m"> | |
</value>, ie. an area of <comp | |
set="area" | |
expr="mul(width, breadth)"></comp> | |
</p> | |
<p>debug: area is now <value read-only property="area"></value></p> | |
<p>consider it over a duration of <value | |
property="duration" | |
value="100" | |
units="s"></value> | |
</p> | |
<p>when stationary, it collects <comp | |
expr="mul(rain_rate, area, duration)"> | |
</comp> | |
</p> | |
<p>now, consider when the object moves at <value | |
property="speed" | |
value="0.1" | |
units-expr="units('m').over('s')"> | |
</value> - that is, in 1 s, it covers a distance of <comp | |
set="distance" | |
expr="mul(speed, value(1, 's'))"></comp></p> | |
<p>let us assume that the object transits <em>instantaneously</em> between discrete positions/steps between the start and end point (spending an equal amount of time at each step), instead of a smooth, continuous motion.<br/> | |
(base case steps=1: object spends duration at start point, then transits to end point) | |
</p> | |
<p>let there be <value | |
property="steps" | |
value="1" | |
units="steps"> | |
</value>. at each step, the object occupies the position for <comp | |
set="duration_per_step" | |
display="duration per step" | |
expr="div(duration, steps)"> | |
</comp>, and receives <comp | |
set="drops_per_step" | |
display="drops per step" | |
expr="mul(rain_rate, area, duration_per_step)"> | |
</comp></p> | |
<p>(try increasing the number of steps to a large number <button ng-click="steps.value = 1e6">eg. 1e6 (1 million)</button>, this approaches a smooth motion)</p> | |
<p>therefore, in total, a moving object receives <comp | |
expr="mul(drops_per_step, steps)"> | |
</comp></p> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Provides an algebra for handling physical quantities. | |
**/ | |
quant_alg = (function() { | |
function Value(name, val, dim) { | |
if (arguments.length === 2) { | |
dim = val; | |
val = name; | |
name = undefined; | |
} | |
if (typeof dim === "string") | |
dim = units(dim); | |
this.name = name; | |
this.value = val; | |
this.units = dim; | |
} | |
function value(name, val, dim) { | |
if (arguments.length === 2) | |
return new Value(name, val); | |
return new Value(name, val, dim); | |
} | |
Value.prototype.toString = function() { | |
return this.value.toString() + ' ' + this.units.toString(); | |
}; | |
function Units(init) { | |
this.val = init || {}; | |
} | |
Units.prototype.toString = function(sep) { | |
return join(_.pairs(this.val), sep, function(pair) { | |
var desc = pair[0], pow = pair[1]; | |
if (pow === 0) | |
return ''; | |
var str = desc.toString(); | |
if (pow !== 1) | |
str += '^' + pow.toString(); | |
return str; | |
}); | |
}; | |
Units.prototype.merge = function(other) { | |
var a = this.val, b = other.val; | |
var ret = _.extend({}, a, b); | |
_.each( | |
// intersection | |
_.reduce(a, function(m, v, k) { | |
if (b.hasOwnProperty(k)) | |
m.push(k); | |
return m; | |
}, []), | |
function(desc) { | |
ret[desc] += a[desc]; | |
}); | |
return new Units(ret); | |
}; | |
function addUnit(top, bottom, desc, pow) { | |
if (desc) { | |
if (pow === undefined) | |
pow = 1; | |
if (pow === 0) | |
return; | |
var receiver; | |
if (pow > 0) | |
receiver = top.val; | |
else { | |
receiver = bottom.val; | |
pow *= -1; | |
} | |
if (receiver.hasOwnProperty(desc)) | |
receiver[desc] += pow; | |
else | |
receiver[desc] = pow; | |
} | |
} | |
function RationalUnits(desc, pow) { | |
this.top = new Units(); | |
this.bottom = new Units(); | |
addUnit(this.top, this.bottom, desc, pow); | |
} | |
function units(desc, pow) { | |
return new RationalUnits(desc, pow); | |
} | |
RationalUnits.prototype.over = function(desc, pow) { | |
addUnit(this.bottom, this.top, desc, pow); | |
return this; | |
}; | |
RationalUnits.prototype.toString = function() { | |
var str1 = this.top.toString(' . '); | |
var str2 = this.bottom.toString(' / '); | |
if (str2.length) | |
str1 = str1 + ' / ' + str2; | |
return str1; | |
}; | |
function join(values, sep, fn) { | |
if (values.length < 1) | |
return ''; | |
return _.reduce(values.slice(1), function(m, v) { | |
return m + sep + fn(v); | |
}, fn(values[0])); | |
} | |
RationalUnits.prototype.cancel = function() { | |
var top = this.top.val, | |
bottom = this.bottom.val; | |
_.each(top, function(pow, desc) { | |
if (!bottom.hasOwnProperty(desc)) | |
return; | |
var pow2 = bottom[desc]; | |
if (pow2 === pow) { | |
delete top[desc]; | |
delete bottom[desc]; | |
} else if (pow2 < pow) { | |
top[desc] -= pow2; | |
delete bottom[desc]; | |
} else { | |
bottom[desc] -= pow; | |
delete top[desc]; | |
} | |
}); | |
}; | |
function mul(_) { | |
return reduce( | |
Array.prototype.slice.call(arguments), | |
' * ', // disp | |
function(a, b) { return a * b }, // valueOp | |
function(a, b) { | |
var u = units(); | |
u.top = a.top.merge(b.top); | |
u.bottom = a.bottom.merge(b.bottom); | |
u.cancel(); | |
return u; | |
} | |
); | |
} | |
function div(_) { | |
return reduce( | |
Array.prototype.slice.call(arguments), | |
' / ', // disp | |
function(a, b) { return a / b }, // valueOp | |
function(a, b) { | |
var u = units(); | |
u.top = a.top.merge(b.bottom); | |
u.bottom = a.bottom.merge(b.top); | |
u.cancel(); | |
return u; | |
} | |
); | |
} | |
function reduce(values, disp, valueOp, unitsOp) { | |
var lines = []; | |
if (values.length < 1 || !_.every(values)) | |
return lines; | |
lines.push(join(values, disp, function(v) { | |
return v.name || v.toString(); | |
})); | |
function addLine(line) { | |
lines.push('= ' + line); | |
return line; | |
} | |
var a = values[0], v; | |
for (var i = 1, b; i < values.length; i++) { | |
addLine(join(values.slice(i-1), disp, function(v) { | |
return v.toString(); | |
})); | |
b = values[i]; | |
v = value(valueOp(a.value, b.value), unitsOp(a.units, b.units)); | |
values[i] = v; | |
a = v; | |
} | |
addLine(v.toString()); | |
return [lines, v]; | |
} | |
return { | |
value: value, | |
units: units, | |
reduce: reduce, | |
mul: mul, | |
div: div | |
}; | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Styles go here */ | |
body { | |
font: 14px helvetica, arial, clean, sans-serif; | |
} | |
.value, .vvalue { | |
display: inline; | |
} | |
.value > .vvalue > input { | |
width: 2em; | |
} | |
.value > .vvalue { | |
margin: 0 0.5em; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment