fieldkit
Version:
Basic building blocks for computational design projects. Written in CoffeeScript for browser and server environments.
406 lines (322 loc) • 10.6 kB
JavaScript
// Generated by CoffeeScript 1.6.3
(function() {
var Tempo, Time, Timer, Timespan, math, util,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
util = require('./util');
math = require('./math/math');
/*
Timer: keeps track of time, measures intervals etc.
------------------------------------------------------------------------------
*/
Timer = (function() {
Timer.prototype.now = null;
Timer.prototype.prev = null;
function Timer() {
this.reset();
}
Timer.prototype.update = function() {
var dt;
this.now = Date.now();
dt = this.now - this.prev;
this.prev = this.now;
return dt;
};
Timer.prototype.elapsed = function() {
return Date.now() - this.prev;
};
Timer.prototype.reset = function() {
return this.now = this.prev = Date.now();
};
return Timer;
})();
/*
Tempo: keeps track of rhythm, converts between beats, bars, time, tempo etc
------------------------------------------------------------------------------
*/
Tempo = (function() {
Tempo.prototype.bpm = 120;
Tempo.prototype.sigNum = 4;
Tempo.prototype.sigDenom = 4;
Tempo.prototype.resolution = 32;
Tempo.prototype.beatInterval = 0;
Tempo.prototype.gridInterval = 0;
Tempo.prototype.time = 0;
Tempo.prototype.prevEvent = 0;
Tempo.prototype.beats = 0;
Tempo.prototype.bars = 0;
Tempo.prototype.beat = 0;
Tempo.prototype.onBeat = false;
Tempo.prototype.onBar = false;
Tempo.prototype.on64 = false;
Tempo.prototype.on32 = false;
Tempo.prototype.on16 = false;
Tempo.prototype.on8 = false;
Tempo.prototype.on4 = false;
Tempo.prototype.on2 = false;
function Tempo(bpm, sigNum, sigDenom, resolution) {
this.bpm = bpm != null ? bpm : 120;
this.sigNum = sigNum != null ? sigNum : 4;
this.sigDenom = sigDenom != null ? sigDenom : 4;
this.resolution = resolution != null ? resolution : 32;
this.reset();
}
Tempo.prototype.reset = function() {
this.beatInterval = 1000 / (this.bpm / 60);
this.gridInterval = this.beatInterval * this.sigNum / this.resolution;
this.time = this.prevEvent = 0;
this.beats = 0;
this.beat = this.bars = 0;
this.onBeat = this.onBar = false;
return this.on64 = this.on32 = this.on16 = this.on8 = this.on4 = this.on2 = false;
};
Tempo.prototype.update = function(dt) {
var forceOnGrid;
forceOnGrid = this.time - this.prevEvent >= this.gridInterval;
this.setTime(this.time + dt, forceOnGrid);
return this.beat;
};
Tempo.prototype.setTime = function(time, forceOnGrid) {
var gridUnits, r, u;
this.time = time;
if (this.time % this.gridInterval === 0 || forceOnGrid) {
this.prevEvent = time;
gridUnits = Math.floor(this.time / this.gridInterval);
u = gridUnits;
r = this.resolution;
this.beats = Math.floor(this.time / this.beatInterval);
this.bars = Math.floor(this.beats / this.sigDenom);
this.beat = this.beats % this.sigNum;
this.onBeat = u % (r / this.sigNum) === 0;
this.onBar = (u % this.resolution) === 0;
this.on64 = u % (r / 64) === 0;
this.on32 = u % (r / 32) === 0;
this.on16 = u % (r / 16) === 0;
this.on8 = u % (r / 8) === 0;
this.on4 = u % (r / 4) === 0;
this.on2 = u % (r / 2) === 0;
} else {
this.onBeat = this.onBar = false;
this.on64 = this.on32 = this.on16 = this.on8 = this.on4 = this.on2 = false;
}
return this.beat;
};
return Tempo;
})();
/*
Time: Represents a single moment in time
------------------------------------------------------------------------------
*/
Time = (function(_super) {
__extends(Time, _super);
Time.prototype.value = 0;
function Time(arg, fps, tempo) {
this.set(arg, fps, tempo);
}
Time.prototype.set = function(arg, fps, tempo) {
if (arg == null) {
arg = 0;
}
return this.value = (function() {
switch (typeof arg) {
case 'number':
return arg;
case 'string':
return this["eval"](arg, fps, tempo);
default:
return arg.value;
}
}).call(this);
};
Time.prototype.add = function(time) {
return this.value += typeof time === 'number' ? time : time.value;
};
Time.prototype.add_ = function(time) {
return new Time(this.value + (typeof time === 'number' ? time : time.value));
};
Time.prototype.sub = function(time) {
return this.time -= typeof time === 'number' ? time : time.value;
};
Time.prototype.sub_ = function(time) {
return new Time(this.value - (typeof time === 'number' ? time : time.value));
};
Time.prototype.scale = function(factor) {
return this.value *= factor;
};
Time.prototype.scale_ = function(factor) {
return new Time(this.value * factor);
};
Time.prototype.clone = function() {
return new Time(this.value);
};
Time.prototype.equals = function(time) {
return this.value === (typeof time === 'number' ? time : time.value);
};
Time.prototype.toString = function() {
return "" + this.value + "ms";
};
Time.prototype.normalizedTo = function(span) {
return math.fit(this.value, span.from.value, span.to.value, 0, 1);
};
Time.set('s', function(seconds) {
return this.value = seconds * 1000;
});
Time.get('s', function() {
return this.value / 1000;
});
Time.prototype.toFrame = function(fps) {
if (fps == null) {
fps = 60;
}
return Math.floor(this.value / (1000 / fps));
};
Time.prototype["eval"] = function(string, fps, tempo) {
var interval, re, unit, units, _i, _len;
if (tempo == null) {
tempo = null;
}
units = [
{
symbol: 'ms',
factor: 1
}, {
symbol: 's',
factor: 1000
}, {
symbol: 'm',
factor: 60000
}, {
symbol: 'f',
factor: 1000 / fps
}
];
if (tempo != null) {
interval = tempo.gridInterval;
units.push({
symbol: 'i',
factor: interval
});
units.push({
symbol: 'n',
factor: interval * tempo.resolution
});
}
for (_i = 0, _len = units.length; _i < _len; _i++) {
unit = units[_i];
re = new RegExp("\\d+(?=" + unit.symbol + ")", "g");
string = string.replace(re, function(value) {
return value * unit.factor;
});
re = new RegExp(unit.symbol, "g");
string = string.replace(re, '');
}
return eval(string);
};
Time.s = function(seconds) {
return new Time(seconds * 1000);
};
Time.ms = function(milliseconds) {
return new Time(milliseconds);
};
Time.f = function(frame, fps) {
return new Time(frame / (1000 / fps));
};
Time.i = function(intervals, tempo) {
return new Time(intervals * tempo.gridInterval);
};
Time.str = function(string, fps, tempo) {
return new Time(string, fps, tempo);
};
return Time;
})(util.EXObject);
/*
Timespan: Represents a duration of time between two moments
------------------------------------------------------------------------------
*/
Timespan = (function(_super) {
__extends(Timespan, _super);
function Timespan() {
switch (arguments.length) {
case 0:
this.from = new Time(0);
this.to = new Time(0);
break;
case 1:
this.from = new Time(0);
this.to = new Time(arguments[0]);
break;
case 2:
this.from = new Time(arguments[0]);
this.to = new Time(arguments[1]);
}
}
Timespan.prototype.segmentByInterval = function(interval, snapEnd) {
var current, halfInterval, last, segments;
if (snapEnd == null) {
snapEnd = false;
}
interval = typeof interval === 'number' ? new Time(interval) : interval;
segments = [];
current = new Timespan(this.from, this.from.add_(interval));
segments.push(current.clone());
while (current.to.value < this.to.value) {
current.from.add(interval);
current.to.add(interval);
segments.push(current.clone());
}
if (snapEnd && segments.length > 0) {
last = segments[segments.length - 1];
halfInterval = interval.scale_(0.5);
if (this.to.value - last.to.value > halfInterval) {
segments.push(new Timespan(last.clone(), this.to.clone()));
} else {
last.to.value = this.to.value;
}
}
return segments;
};
Timespan.prototype.overlaps = function(other) {
var from, from2, to, to2;
from = this.from.value;
to = this.to.value;
from2 = other.from.value;
to2 = other.to.value;
return (to2 >= from && to2 <= to) || (from2 >= from && from2 <= to) || (from2 <= from && to2 >= to);
};
Timespan.prototype.clone = function() {
return new Timespan(this.from, this.to);
};
Timespan.prototype.toString = function() {
return "Timespan(" + this.from + " - " + this.to + ")";
};
Timespan.set('length', function(length) {
return this.to.value = this.from.value + length;
});
Timespan.get('length', function() {
return new Time(this.to.value - this.from.value);
});
Timespan.get('s', function() {
return this.length.s;
});
Timespan.s = function(seconds) {
return new Timespan(seconds * 1000);
};
Timespan.str = function(string, fps, tempo) {
var from, times, to;
times = string.split('..');
if (times.length !== 2) {
throw "Invalid argument: " + string;
}
from = Time.str(times[0], fps, tempo);
to = Time.str(times[1], fps, tempo);
return new Timespan(from, to);
};
return Timespan;
})(util.EXObject);
module.exports = {
Timer: Timer,
Tempo: Tempo,
Time: Time,
Timespan: Timespan
};
}).call(this);