UNPKG

fieldkit

Version:

Basic building blocks for computational design projects. Written in CoffeeScript for browser and server environments.

406 lines (322 loc) 10.6 kB
// 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);