├── .DS_Store
├── .project
├── README.md
├── css
├── .DS_Store
├── img
│ ├── circles.png
│ └── cubes.jpg
└── jamboree.css
├── jamboree.html
└── js
├── .DS_Store
├── app.js
├── commonUtils
├── .DS_Store
├── commonUtils.js
├── kcolor.js
├── map.js
├── range.js
├── rect.js
├── time.js
├── timespan.js
├── transform.js
├── tree.js
├── ui.js
├── utilities.js
└── vector.js
├── jamboreeUI.js
├── libs
├── dancer.js
├── inheritance.js
├── jquery-1.10.1.js
├── jquery-1.10.2.min.js
├── jquery-1.6.2.js
├── jquery-migrate-1.2.1.js
├── jquery-ui.js
├── jquery.mousewheel.min.js
├── jsxTransformer.js
├── poly2tri.js
├── processing-1.4.1.js
├── react.js
├── require.js
├── simplex_noise.js
├── three.js
└── underscore.js
├── main.js
├── settings.js
└── tutorials
├── boids.js
├── flowers.js
├── gameOfLife.js
├── lTrees.js
├── terrain.js
└── tutorials.js
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/galaxykate/PCGTutorials/92a51d3769259dfaee0dfa695830f897915c011a/.DS_Store
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Jamboree
4 |
5 |
6 |
7 |
8 |
9 | com.aptana.ide.core.unifiedBuilder
10 |
11 |
12 |
13 |
14 |
15 | com.aptana.projects.webnature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | PCGTutorials
2 | ============
3 |
4 | PCGTutorials
5 | A set of procedural generation tutorials by GalaxyKate
6 |
--------------------------------------------------------------------------------
/css/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/galaxykate/PCGTutorials/92a51d3769259dfaee0dfa695830f897915c011a/css/.DS_Store
--------------------------------------------------------------------------------
/css/img/circles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/galaxykate/PCGTutorials/92a51d3769259dfaee0dfa695830f897915c011a/css/img/circles.png
--------------------------------------------------------------------------------
/css/img/cubes.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/galaxykate/PCGTutorials/92a51d3769259dfaee0dfa695830f897915c011a/css/img/cubes.jpg
--------------------------------------------------------------------------------
/css/jamboree.css:
--------------------------------------------------------------------------------
1 | .noSelect {
2 | -webkit-user-select:none;
3 | -khtml-user-select:none;
4 | -moz-user-select:none;
5 | -o-user-select:none;
6 | outline:none;
7 | user-select:none;
8 | }
9 | body {
10 | background-image:url('img/circles.png');
11 | /*background-image: url('img/carved2.jpeg');*/
12 | background-repeat:repeat;
13 | font-family:"Trebuchet MS",Helvetica,sans-serif;
14 | }
15 | #app {
16 | position:relative;
17 | box-shadow:0px 0px 20px 3px rgba(255, 205, 255, .4);
18 | margin:10px auto;
19 | border-radius:30px;
20 | /*border:5px solid rgba(255, 255, 255, .4);*/
21 | }
22 | .appSize {
23 | width:800px;
24 | height:480px;
25 | overflow:show;
26 | }
27 | #processingHolder {
28 | border-radius:30px;
29 | overflow:hidden;
30 | }
31 | #viewProcessing {
32 | border-radius:30px;
33 | width:100%;
34 | height:100%;
35 | }
36 | #controlBar {
37 | position:relative;
38 | top:-30px;
39 | width:600px;
40 | height:40px;
41 | background-color:rgba(20, 20, 20, .9);
42 | box-shadow:0px 2px 10px rgba(255, 144, 255, .4);
43 | margin:10px auto;
44 | border-radius:30px;
45 | }
46 | #controlBar button {
47 | font-size:17px;
48 | position:absolute;
49 | top:5px;
50 | background-color:rgba(20, 20, 20, .9);
51 | box-shadow:0px 2px 3px rgba(255, 144, 255, .4);
52 | border-radius:30px;
53 | color:white;
54 | width:30px;
55 | height:30px;
56 | }
57 | #backButton {
58 | left:10px;
59 | }
60 | #forwardButton {
61 | right:10px;
62 | }
63 | #title {
64 | position:relative;
65 | width:180px;
66 | margin:0 auto;
67 | text-align:center;
68 | font-size:17px;
69 | top:5px;
70 | color:white;
71 | }
72 |
--------------------------------------------------------------------------------
/jamboree.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 | Verba docent, exempla trahunt
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/js/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/galaxykate/PCGTutorials/92a51d3769259dfaee0dfa695830f897915c011a/js/.DS_Store
--------------------------------------------------------------------------------
/js/app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Kate Compton
3 | */
4 |
5 | var app = {};
6 |
7 | define(["./jamboreeUI", "./settings", "./tutorials/tutorials"], function(UI, _settings, tutorials) {'use strict';
8 |
9 | // Active tutorials
10 | var tutorialNames = ["GameOfLife", "Flowers", "LTrees", "Terrain", "Boids"];
11 | // Starting
12 | var tutorialIndex = null;
13 |
14 | app.init = function() {
15 | console.log("Init Jamboree App");
16 | app.time = {
17 | total : 0,
18 | framecount : 0,
19 | elapsed : .1,
20 | };
21 |
22 | app.elapsed = .001;
23 | app.ui = UI;
24 | app.ui.init();
25 |
26 | var appContainer = $("#app");
27 | app.w = appContainer.width();
28 | app.h = appContainer.height();
29 |
30 | app.startTime = new Date().getTime();
31 |
32 | var startIndex = localStorage.getItem("lastTutorial");
33 | if (startIndex === null)
34 | startIndex = 0;
35 |
36 | app.loadTutorial(startIndex);
37 |
38 | };
39 |
40 | app.loadTutorial = function(index) {
41 | console.log("Load " + index);
42 | tutorialIndex = index;
43 | var type = tutorialNames[index];
44 | app.tutorial = new tutorials[type]();
45 | $("#title").html(app.tutorial.title);
46 |
47 | localStorage.setItem("lastTutorial", index);
48 |
49 | };
50 |
51 | app.nextTutorial = function() {
52 | tutorialIndex = (tutorialIndex + 1) % tutorialNames.length;
53 | app.loadTutorial(tutorialIndex);
54 | };
55 |
56 | app.previousTutorial = function() {
57 | tutorialIndex = (tutorialNames.length + tutorialIndex - 1) % tutorialNames.length;
58 | app.loadTutorial(tutorialIndex);
59 | };
60 |
61 | app.setTime = function() {
62 | app.time.framecount++;
63 |
64 | var last = app.time.total;
65 | app.time.total = (new Date().getTime() - app.startTime) * .001;
66 | app.time.elapsed = app.time.total - last;
67 |
68 | };
69 |
70 | app.update = function() {
71 | app.setTime();
72 | app.ui.update(app.time);
73 |
74 | };
75 | });
76 |
--------------------------------------------------------------------------------
/js/commonUtils/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/galaxykate/PCGTutorials/92a51d3769259dfaee0dfa695830f897915c011a/js/commonUtils/.DS_Store
--------------------------------------------------------------------------------
/js/commonUtils/commonUtils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Kate Compton
3 | */
4 |
5 | var utilities = {};
6 | var prefix = "commonUtils/";
7 | define(["inheritance", "noise", prefix + "transform", prefix + "vector", prefix + "rect", prefix + "kcolor", prefix + "utilities", prefix + "ui", "jQueryUI", "underscore"], function(Inheritance, _Noise, _Transform, _Vector, _Rect, _KColor, _utilities, uiUtils, JQUERY, _) {
8 | var common = {
9 | Vector : _Vector,
10 | KColor : _KColor,
11 | Rect : _Rect,
12 |
13 | Transform : _Transform,
14 |
15 | };
16 | console.log(uiUtils);
17 | $.extend(utilities, _utilities);
18 | $.extend(utilities, common);
19 | $.extend(utilities, uiUtils);
20 | //=============================================================
21 | //=============================================================
22 | //=============================================================
23 | // Add watching
24 | /*
25 | * object.watch polyfill
26 | *
27 | * 2012-04-03
28 | *
29 | * By Eli Grey, http://eligrey.com
30 | * Public Domain.
31 | * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
32 | */
33 |
34 | // object.watch
35 | if (!Object.prototype.watch) {
36 | Object.defineProperty(Object.prototype, "watch", {
37 | enumerable : false,
38 | configurable : true,
39 | writable : false,
40 | value : function(prop, handler) {
41 | var oldval = this[prop], newval = oldval, getter = function() {
42 | return newval;
43 | }, setter = function(val) {
44 | oldval = newval;
45 | return newval = handler.call(this, prop, oldval, val);
46 | };
47 |
48 | if (
49 | delete this[prop]) {// can't watch constants
50 | Object.defineProperty(this, prop, {
51 | get : getter,
52 | set : setter,
53 | enumerable : true,
54 | configurable : true
55 | });
56 | }
57 | }
58 | });
59 | }
60 |
61 | // object.unwatch
62 | if (!Object.prototype.unwatch) {
63 | Object.defineProperty(Object.prototype, "unwatch", {
64 | enumerable : false,
65 | configurable : true,
66 | writable : false,
67 | value : function(prop) {
68 | var val = this[prop];
69 | delete this[prop];
70 | // remove accessors
71 | this[prop] = val;
72 | }
73 | });
74 | }
75 |
76 | //=============================================================
77 | //=============================================================
78 | //=============================================================
79 |
80 | utilities.noiseObj = new _Noise();
81 | utilities.noise = function() {
82 | // use the correct number of args
83 | switch(arguments.length) {
84 | case 1:
85 | return utilities.noiseObj.noise2D(arguments[0], 1000);
86 | break;
87 | case 2:
88 | return utilities.noiseObj.noise2D(arguments[0], arguments[1]);
89 | break;
90 | case 3:
91 | return utilities.noiseObj.noise3D(arguments[0], arguments[1], arguments[2]);
92 | break;
93 | case 4:
94 | return utilities.noiseObj.noise4D(arguments[0], arguments[1], arguments[2], arguments[3]);
95 | break;
96 | default:
97 | console.log("Attempting to use Noise with " + arguments.length + " arguments: not supported!");
98 | return 0;
99 | break;
100 | }
101 | };
102 |
103 | // renormalized to between [0, 1]
104 | utilities.unitNoise = function() {
105 | return utilities.noise.apply(undefined, arguments) * .5 + .5;
106 | };
107 |
108 | //==================================================
109 | return utilities;
110 | });
111 |
--------------------------------------------------------------------------------
/js/commonUtils/kcolor.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Kate Compton
3 | */
4 |
5 | // Color utility class
6 | // Create KColors (stored as HSBA [0, 1], so that (.45, .3, 1, .5) would be a half-transparent sky-blue)
7 |
8 | define([], function() {
9 |
10 | function toRGB(h, s, v) {
11 | var r, g, b;
12 | h *= 6;
13 | h = h % 6;
14 |
15 | var i = Math.floor(h);
16 | var f = h - i;
17 | var p = v * (1 - s);
18 | var q = v * (1 - (s * f));
19 | var t = v * (1 - (s * (1 - f)));
20 | if (i == 0) {
21 | r = v;
22 | g = t;
23 | b = p;
24 | } else if (i == 1) {
25 | r = q;
26 | g = v;
27 | b = p;
28 | } else if (i == 2) {
29 | r = p;
30 | g = v;
31 | b = t;
32 | } else if (i == 3) {
33 | r = p;
34 | g = q;
35 | b = v;
36 | } else if (i == 4) {
37 | r = t;
38 | g = p;
39 | b = v;
40 | } else if (i == 5) {
41 | r = v;
42 | g = p;
43 | b = q;
44 | }
45 | r = Math.floor(r * 255);
46 | g = Math.floor(g * 255);
47 | b = Math.floor(b * 255);
48 | return [r, g, b];
49 | };
50 |
51 | return (function() {
52 |
53 | // Private functions
54 |
55 | // Make the Vector class
56 | function KColor(h, s, b, a) {
57 | this.h = h;
58 | this.s = s;
59 | this.b = b;
60 | if (a !== undefined)
61 | this.a = a;
62 | else
63 | this.a = 1;
64 | };
65 |
66 | // Add lots of utilty, modification, lerping, etc functions to deal with colors
67 |
68 | KColor.prototype.toString = function() {
69 | return "hsb: " + this.h.toFixed(2) + " " + this.s.toFixed(2) + " " + this.b.toFixed(2) + " " + this.a.toFixed(2);
70 |
71 | };
72 |
73 | KColor.prototype.clone = function() {
74 | return new KColor(this.h, this.s, this.b, this.a);
75 | };
76 |
77 | KColor.prototype.constrainToUnit = function(v) {
78 | return Math.min(Math.max(v, 0), 1);
79 | };
80 |
81 | KColor.prototype.cloneShade = function(shade, fade) {
82 | var clone;
83 |
84 | this.use(function(h, s, b, a) {
85 | clone = new KColor(h, s, b, a);
86 | }, shade, fade);
87 |
88 | return clone;
89 | };
90 |
91 | KColor.prototype.creatMutant = function() {
92 | var clone;
93 |
94 | var hMutation = .2 * (Math.random() - .5);
95 | var sMutation = .2 * (Math.random() - .5);
96 | var bMutation = .2 * (Math.random() - .5);
97 |
98 | return new KColor(this.h + hMutation, this.s + sMutation, this.b + bMutation, this.a);
99 |
100 | };
101 |
102 | // shade goes from -1 to 1, as does fade.
103 | KColor.prototype.fill = function(g, shade, fade) {
104 | return this.use(g.fill, shade, fade);
105 | };
106 |
107 | KColor.prototype.stroke = function(g, shade, fade) {
108 | return this.use(g.stroke, shade, fade);
109 | };
110 |
111 | KColor.prototype.background = function(g, shade, fade) {
112 | return this.use(g.background, shade, fade);
113 | };
114 |
115 | KColor.prototype.use = function(gFunc, shade, fade) {
116 |
117 | var s1 = this.s;
118 | var h1 = this.h;
119 | var b1 = this.b;
120 | var a1 = this.a;
121 |
122 | if (shade !== undefined) {
123 | if (shade > 0) {
124 | s1 = this.constrainToUnit(s1 - shade * (s1));
125 | b1 = this.constrainToUnit(b1 + shade * (1 - b1));
126 | } else {
127 | s1 = this.constrainToUnit(s1 - shade * (1 - s1));
128 | b1 = this.constrainToUnit(b1 + shade * (b1));
129 | }
130 |
131 | h1 = (h1 + -.06 * shade + 1) % 1;
132 | }
133 |
134 | // Increase (or decrease) the alpha for this
135 | if (fade !== undefined) {
136 | if (fade < 0) {
137 | a1 = this.constrainToUnit(a1 * (1 + fade));
138 | } else {
139 | a1 = this.constrainToUnit((1 - a1) * fade + a1);
140 | }
141 | }
142 |
143 | gFunc(h1, s1, b1, a1);
144 | };
145 |
146 | //=================================================================
147 | //=================================================================
148 | //=================================================================
149 |
150 | KColor.prototype.toCSS = function(shade, fade) {
151 |
152 | if (shade !== undefined) {
153 | var css;
154 | this.use(function(h, s, b, a) {
155 | var rgb = toRGB(h, s, b, a);
156 | var vals = "";
157 | $.each(rgb, function(index, val) {
158 | vals += Math.round(val) + ", ";
159 | });
160 | vals += a;
161 | css = "rgba(" + vals + ")";
162 | }, shade, fade);
163 |
164 | return css;
165 | }
166 |
167 | var rgb = toRGB(this.h, this.s, this.b, this.a);
168 | var vals = "";
169 | $.each(rgb, function(index, val) {
170 | vals += Math.round(val) + ", ";
171 | });
172 | vals += this.a;
173 | return "rgba(" + vals + ")";
174 | };
175 |
176 | // From the internet
177 | KColor.prototype.toRGB = function() {
178 | return toRGB(this.h, this.s, this.b, this.a);
179 | };
180 |
181 | var toHexString = function(v) {
182 | var v2 = v.toString(16);
183 | if (v2.length == 0)
184 | v2 = "0" + v2;
185 | if (v2.length == 1)
186 | v2 = "0" + v2;
187 | return v2;
188 | };
189 |
190 | KColor.prototype.toHex = function() {
191 | var rgb = this.toRGB();
192 |
193 | var hex = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
194 | hex = toHexString(rgb[0]) + toHexString(rgb[1]) + toHexString(rgb[2]);
195 | return hex;
196 | };
197 |
198 | KColor.makeIDColor = function(idNumber) {
199 | return new KColor((idNumber * .31299 + .42) % 1, 1, 1);
200 | };
201 |
202 | //=================================================================
203 | //=================================================================
204 | //=================================================================
205 |
206 | return KColor;
207 | })();
208 |
209 | });
210 |
--------------------------------------------------------------------------------
/js/commonUtils/map.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Kate Compton
3 | */
4 |
5 | // Reusable Vector class
6 |
7 | define(["./vector"], function(Vector) {
8 | var Map = Class.extend({
9 | init : function() {
10 | this.values = [];
11 | },
12 |
13 | setDimensions : function(width, height, spacing) {
14 | this.columns = Math.round(width / spacing);
15 | this.rows = Math.round(height / spacing);
16 | this.xSpacing = width / this.columns;
17 | this.ySpacing = height / this.rows;
18 |
19 | for (var i = 0; i < this.columns; i++) {
20 | this.values[i] = [];
21 | for (var j = 0; j < this.rows; j++) {
22 | this.values[i][j] = .5 * (j / this.rows) + .5 * Math.random();
23 | }
24 | }
25 | },
26 |
27 | setFill : function(g, value) {
28 | g.fill((value * 1.5) % 1, .3 + .7 * value, 1 - value);
29 | },
30 |
31 | sampleAt : function(p) {
32 | var x = p.x / this.xSpacing;
33 | var y = p.y / this.ySpacing;
34 | var column = Math.floor(x);
35 | var row = Math.floor(y);
36 | column = utilities.constrain(column, 0, this.columns - 1);
37 | row = utilities.constrain(row, 0, this.rows - 1);
38 | return this.values[column][row];
39 | },
40 |
41 | draw : function(g, t) {
42 | if (app.options.debugDrawLightmap) {
43 | for (var i = 0; i < this.columns; i++) {
44 | for (var j = 0; j < this.rows; j++) {
45 | var v = this.values[i][j];
46 | this.setFill(g, v);
47 | g.rect(i * this.xSpacing, j * this.ySpacing, this.xSpacing, this.ySpacing);
48 | }
49 | }
50 | }
51 | }
52 | })
53 | return Map;
54 |
55 | });
56 |
--------------------------------------------------------------------------------
/js/commonUtils/range.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Kate Compton
3 | */
4 | define(["inheritance"], function(Inheritance) {
5 | var Range = Class.extend({
6 | init : function(context) {
7 |
8 | // default values
9 | this.min = 0;
10 | this.max = 1;
11 | this.defaultValue = .5;
12 |
13 | // Translate all the context into this
14 | $.extend(this, context);
15 | this.value = this.defaultValue;
16 | },
17 |
18 | setToMax : function() {
19 | this.setTo(this.max);
20 | },
21 |
22 | setToMin : function() {
23 | this.setTo(this.min);
24 | },
25 |
26 | setToPct : function(pct) {
27 | this.setTo((this.max - this.min) * pct + this.min);
28 | },
29 |
30 | add : function(val) {
31 | this.setTo(val + this.value);
32 | },
33 |
34 | setTo : function(val) {
35 | this.value = Math.min(this.max, Math.max(this.min, val));
36 | },
37 |
38 | getValue : function() {
39 | return this.value;
40 | },
41 |
42 | getPct : function(v) {
43 | return (v - this.min) / (this.max - this.min);
44 | },
45 |
46 | toString : function() {
47 | return "[" + this.min.toFixed(2) + "-" + this.max.toFixed(2) + "]";
48 | },
49 | });
50 | return Range;
51 | });
52 |
--------------------------------------------------------------------------------
/js/commonUtils/rect.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Kate Compton
3 | */
4 |
5 | // Reusable Vector class
6 |
7 | define(["./vector"], function(Vector) {
8 | var Rect = Class.extend({
9 |
10 | // use as init(x, y, w, h) or init(position:Vector, dimensions:Vector)
11 | init : function() {
12 | if (arguments.length == 4) {
13 | this.x = arguments[0];
14 | this.y = arguments[1];
15 | this.w = arguments[2];
16 | this.h = arguments[3];
17 |
18 | } else if (arguments.length === 2) {
19 | this.x = arguments[0].x;
20 | this.y = arguments[0].y;
21 | this.w = arguments[1].x;
22 | this.h = arguments[1].y;
23 | } else {
24 | this.w = 0;
25 | this.h = 0;
26 | }
27 |
28 | },
29 |
30 | clone : function() {
31 | return new Rect(this.x, this.y, this.w, this.h);
32 | },
33 |
34 | setPosition : function(p) {
35 | if (arguments.length == 2) {
36 | this.x = arguments[0];
37 | this.y = arguments[1];
38 | } else {
39 | this.x = p.x;
40 | this.y = p.y;
41 | }
42 | },
43 |
44 | setDimensions : function(p) {
45 | if (arguments.length == 2) {
46 | this.w = arguments[0];
47 | this.h = arguments[1];
48 | } else {
49 | this.w = p.x;
50 | this.h = p.y;
51 | }
52 | },
53 |
54 | // return the Vectors of the corners
55 | getCorners : function(ccw) {
56 | var x0 = this.x + this.w;
57 | var x1 = this.x;
58 | var y0 = this.y;
59 | var y1 = this.y + this.h;
60 |
61 | if (ccw)
62 | return [new Vector(x0, y0), new Vector(x1, y0), new Vector(x1, y1), new Vector(x0, y1)];
63 | return [new Vector(x0, y0), new Vector(x0, y1), new Vector(x1, y1), new Vector(x1, y0)];
64 | },
65 |
66 | reset : function() {
67 | this.x = undefined;
68 | this.y = undefined;
69 | this.w = 0;
70 | this.h = 0;
71 | },
72 |
73 | stretchToContainBox : function(b) {
74 | var box = this;
75 | var c = b.getCorners();
76 | $.each(c, function(index, corner) {
77 | box.stretchToContainPoint(corner);
78 | });
79 | },
80 |
81 | stretchToContainPoint : function(pt) {
82 | if (this.x === undefined)
83 | this.x = pt.x;
84 | if (this.y === undefined)
85 | this.y = pt.y;
86 |
87 | if (pt.x < this.x) {
88 | this.w += this.x - pt.x;
89 | this.x = pt.x;
90 | }
91 | if (pt.y < this.y) {
92 | this.h += this.y - pt.y;
93 | this.y = pt.y;
94 | }
95 |
96 | this.w = Math.max(this.w, pt.x - this.x);
97 | this.h = Math.max(this.h, pt.y - this.y);
98 |
99 | },
100 |
101 | stretchToContainPoints : function(pts) {
102 | for (var i = 0; i < pts.length; i++) {
103 | this.stretchToContainPoint(pts[i]);
104 | }
105 | },
106 |
107 | getRandomPosition : function(border) {
108 | var x = this.x + border + Math.random() * (this.w - border * 2);
109 | var y = this.y + border + Math.random() * (this.h - border * 2);
110 |
111 | return new Vector(x, y);
112 | },
113 |
114 | getSidePosition : function(side, sidePos, outset) {
115 | var x = sidePos;
116 |
117 | if (side === "left") {
118 | x = outset;
119 | }
120 | if (side === "right") {
121 | x = this.w - outset;
122 | }
123 |
124 | var y = sidePos;
125 |
126 | if (side === "top") {
127 | y = outset;
128 | }
129 | if (side === "bottom") {
130 | y = this.h - outset;
131 | }
132 |
133 | var p = new Vector(x + this.x, y + this.y);
134 | return p;
135 | },
136 |
137 | centerTransform : function(g) {
138 | g.translate(-this.x + -this.w / 2, -this.y + -this.h / 2);
139 | },
140 |
141 | getCenter : function() {
142 | return new Vector(this.x + this.w / 2, this.y + this.h / 2);
143 | },
144 |
145 | draw : function(g) {
146 | g.rect(this.x, this.y, this.w, this.h);
147 | },
148 |
149 | toCSS : function() {
150 | return {
151 | width : this.w + "px",
152 | height : this.h + "px",
153 | top : this.y + "px",
154 | left : this.x + "px",
155 |
156 | };
157 | },
158 | toString : function() {
159 | return "[(" + this.x.toFixed(2) + "," + this.y.toFixed(2) + "), " + this.w.toFixed(2) + "x" + this.h.toFixed(2) + "]";
160 | }
161 | });
162 |
163 | return Rect;
164 |
165 | });
166 |
--------------------------------------------------------------------------------
/js/commonUtils/time.js:
--------------------------------------------------------------------------------
1 | define(["inheritance", "./timespan"], function(Inheritance, TimeSpan) {
2 | var Time = Class.extend({
3 | init : function(name) {
4 | this.name = name;
5 | this.ellapsed = 0;
6 | this.frameCount = 0;
7 | this.total = 0;
8 | this.timespans = new TimeSpan.Manager();
9 |
10 | },
11 |
12 | addTimeSpan : function(timespan) {
13 | this.timespans.add(timespan);
14 | },
15 |
16 | addEllapsed : function(t) {
17 | this.ellapsed = t;
18 | this.total += t;
19 | },
20 |
21 | updateTime : function(t) {
22 | if (isNaN(t)) {
23 | throw ("Update time " + this.name + " with bad total value " + t);
24 | }
25 |
26 | this.ellapsed = t - this.total;
27 |
28 | if (isNaN(this.ellapsed) || this.ellapsed < .001 || this.ellapsed > 1) {
29 | // throw ("Update time " + this.name + " with bad ellapsed value " + this.ellapsed);
30 |
31 | }
32 | if (this.ellapsed < .01)
33 | this.ellapsed = .01;
34 | if (this.ellapsed > .5)
35 | this.ellapsed = .5;
36 | if (isNaN(this.ellapsed))
37 | this.ellapsed = .05;
38 |
39 | this.total = t;
40 | this.timespans.update(this.ellapsed);
41 | },
42 | toString : function() {
43 | return this.name + ": " + this.total.toFixed(2) + "(" + this.ellapsed.toFixed(3) + ")";
44 | }
45 | });
46 |
47 | return Time;
48 | });
49 |
--------------------------------------------------------------------------------
/js/commonUtils/timespan.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Kate Compton
3 | */
4 | define(["inheritance"], function(Inheritance) {
5 |
6 | var TimeSpan = Class.extend({
7 |
8 | // context options:
9 | // onStart, onChange, onFinish, lifespan
10 | init : function(context) {
11 |
12 | // default values
13 | this.lifespan = 1;
14 | this.total = 0;
15 | // Translate all the context into this
16 | $.extend(this, context);
17 | },
18 |
19 | start : function(startTime) {
20 | this.startTime = startTime;
21 | this.total = 0;
22 |
23 | if (this.onStart)
24 | this.onStart(startTime);
25 | },
26 |
27 | increment : function(ellapsed) {
28 | this.total += ellapsed;
29 | if (this.onChange)
30 | this.onChange(this.total, this.getPct());
31 |
32 | if (this.total > this.lifespan)
33 | this.finish();
34 | },
35 |
36 | finish : function() {
37 | this.completed = true;
38 |
39 | if (this.onFinish)
40 | this.onFinish();
41 |
42 | },
43 |
44 | getPct : function() {
45 | return (this.total) / this.lifespan;
46 | },
47 |
48 | drawClock : function(g, center, radius) {
49 | var pct = this.getPct();
50 | g.fill(0);
51 | g.ellipse(center.x, center.y, radius, radius);
52 | g.fill(1);
53 | g.arc(center.x, center.y, radius - 1, radius - 1, 0, 2 * pct * Math.PI);
54 | g.fill(0);
55 | g.ellipse(center.x, center.y, radius * .2, radius * .2);
56 |
57 | },
58 |
59 | toString : function() {
60 | return this.total + "/" + this.lifespan + " = " + this.getPct();
61 | },
62 | });
63 |
64 | var TimeSpanManager = Class.extend({
65 | init : function() {
66 | this.timespans = [];
67 | },
68 |
69 | add : function(timespan) {
70 | this.timespans.push(timespan);
71 | },
72 |
73 | update : function(ellapsed) {
74 | $.each(this.timespans, function(index, t) {
75 | t.increment(ellapsed);
76 | });
77 |
78 | // cleanup
79 | this.timespans = _.filter(this.timespans, function(t) {
80 | return !t.completed;
81 | });
82 | }
83 | });
84 |
85 | var PowerUpDown = TimeSpan.extend({
86 | init : function(context) {
87 | this._super(context);
88 | this.ended = false;
89 | this.started = false;
90 | this.total = 0;
91 |
92 | },
93 |
94 | end : function() {
95 | this.ended = true;
96 | this.total = 0;
97 |
98 | },
99 |
100 | getPct : function() {
101 | if (!this.started) {
102 | // Powering up
103 | return utilities.constrain(this.total / this.powerUpLength, 0, 1);
104 |
105 | } else {
106 | if (!this.ended) {
107 | // Cycling
108 | return 1;
109 | } else {
110 | return utilities.constrain(1 - this.total / this.powerDownLength, 0, 1);
111 |
112 | }
113 | }
114 | },
115 |
116 | startMiddle : function() {
117 | this.started = true;
118 | if (this.onMiddle)
119 | this.onMiddle(this.total);
120 |
121 | },
122 |
123 | increment : function(ellapsed) {
124 | this.total += ellapsed;
125 | if (!this.started) {
126 | // Check for start
127 | if (this.total > this.powerUpLength)
128 | this.startMiddle();
129 | } else {
130 | if (this.ended) {
131 | // Check for end
132 | if (this.total > this.powerDownLength) {
133 | this.finish();
134 | }
135 |
136 | }
137 | }
138 |
139 | }
140 | });
141 |
142 | TimeSpan.Manager = TimeSpanManager;
143 | TimeSpan.PowerUpDown = PowerUpDown;
144 | return TimeSpan;
145 | });
146 |
--------------------------------------------------------------------------------
/js/commonUtils/transform.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Kate Compton
3 | */
4 | // Reusable Vector class
5 |
6 | define(["./vector"], function(Vector) {
7 | var Transform = Vector.extend({
8 | init : function(arg0, arg1, arg2) {
9 | this._super(arg0, arg1, arg2);
10 | this.rotation = 0;
11 | this.scale = 1;
12 |
13 | },
14 |
15 | cloneFrom : function(t) {
16 | this.rotation = t.rotation;
17 | this.scale = t.scale;
18 | this.setTo(t);
19 | },
20 |
21 | reset : function() {
22 | this.rotation = 0;
23 | this.scale = 1;
24 | this.setTo(0, 0, 0);
25 | },
26 |
27 | applyTransform : function(g) {
28 | this.translateTo(g);
29 | g.rotate(this.rotation);
30 | g.scale(this.scale);
31 | },
32 |
33 | toWorld : function(localPos, worldPos) {
34 | worldPos.setTo(localPos);
35 | worldPos.rotate(this.rotation);
36 | worldPos.add(this);
37 |
38 | worldPos.mult(this.scale);
39 | if (localPos.rotation !== undefined)
40 | worldPos.rotation += this.rotation;
41 | },
42 |
43 | toLocal : function(worldPos, localPos) {
44 | localPos.setTo(worldPos);
45 | localPos.div(this.scale);
46 | localPos.sub(this);
47 | localPos.rotate(-this.rotation);
48 |
49 | if (worldPos.rotation !== undefined)
50 | localPos.rotation -= this.rotation;
51 | },
52 |
53 | toString : function() {
54 | return "[" + this._super() + " " + this.rotation.toFixed(2) + "rad " + this.scale + "X]";
55 | }
56 | });
57 |
58 | return Transform;
59 |
60 | });
61 |
--------------------------------------------------------------------------------
/js/commonUtils/tree.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Kate Compton
3 | */
4 |
5 | define(["common"], function(common) {
6 | var nodeCount = 0;
7 |
8 | var Tree = Class.extend({
9 | init : function() {
10 | // Set the depth if there's a parent
11 | this.depth = 0;
12 |
13 | this.idNumber = nodeCount;
14 | this.children = [];
15 | nodeCount++;
16 | },
17 |
18 | compileNodes : function(list, query) {
19 |
20 | if (query(this))
21 | list.push(this);
22 | if (this.children) {
23 | $.each(this.children, function(index, child) {
24 | child.compileNodes(list, query);
25 | });
26 | }
27 | },
28 |
29 | removeChildren : function() {
30 | $.each(this.children, function(index, child) {
31 | child.removeParent();
32 | });
33 | this.children = [];
34 | },
35 |
36 | removeParent : function() {
37 | this.parent = undefined;
38 | this.depth = 0;
39 | },
40 |
41 | setParent : function(parent) {
42 |
43 | this.parent = parent;
44 | this.depth = this.parent !== undefined ? this.parent.depth + 1 : 0;
45 |
46 | // this NEEDS TO HAVE CHILDREN DEFINED
47 | if (this.parent) {
48 | if (this.parent.children === undefined)
49 | this.parent.children = [];
50 |
51 | this.parent.children.push(this);
52 | }
53 | },
54 |
55 | getChildren : function() {
56 | return this.children;
57 | },
58 |
59 | debugPrint : function() {
60 | var spacer = "";
61 | for (var i = 0; i < this.depth; i++) {
62 | spacer += " ";
63 | }
64 | console.log(spacer + this);
65 |
66 | var children = this.getChildren();
67 | if (children !== undefined) {
68 | $.each(children, function(index, node) {
69 | node.debugPrint();
70 | });
71 | }
72 | },
73 |
74 | reduceDown : function(f, base) {
75 | console.log(this.depth + " Reduce down " + this);
76 | base = f(base, this);
77 |
78 | if (this.children !== undefined) {
79 | $.each(this.children, function(index, node) {
80 | base = node.reduceDown(f, base);
81 | });
82 | }
83 |
84 | return base;
85 | },
86 |
87 | toString : function() {
88 | return "Node" + this.idNumber;
89 | },
90 |
91 | generateTree : function(initNode) {
92 | initNode(this);
93 | var children = this.getChildren();
94 | $.each(children, function(index, node) {
95 | node.generateTree(initNode);
96 | });
97 | }
98 | });
99 |
100 | return Tree;
101 |
102 | });
103 |
--------------------------------------------------------------------------------
/js/commonUtils/ui.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Kate Compton
3 | */
4 |
5 | define([], function() {
6 | return {
7 | addSlider : function(parent, key, defaultValue, minValue, maxValue, onChange) {
8 |
9 | // Create an empty slider div
10 | var optionDiv = $("", {
11 | });
12 | optionDiv.css({
13 | "pointer-events" : "auto"
14 | });
15 | parent.append(optionDiv);
16 |
17 | var slider = $('', {
18 | id : 'slider_' + key,
19 | class : "tuning_slider",
20 | value : key
21 | });
22 |
23 | var step = maxValue - minValue;
24 | if (step < 10)
25 | step = .1;
26 | else
27 | step = 1;
28 |
29 | slider.appendTo(optionDiv);
30 | slider.slider({
31 | range : "min",
32 | value : defaultValue,
33 | min : minValue,
34 | max : maxValue,
35 | step : step,
36 | slide : function(event, ui) {
37 | $("#" + key + "amt").text(ui.value);
38 | console.log("Slide " + ui.value);
39 | if (onChange !== undefined) {
40 | onChange(key, ui.value);
41 | }
42 | }
43 | });
44 |
45 | // Create a lable
46 | $('', {
47 | 'for' : 'slider_' + key,
48 | text : key + ": "
49 | }).appendTo(optionDiv);
50 |
51 | // Create a lable
52 | $('', {
53 | id : key + "amt",
54 | text : defaultValue
55 | }).appendTo(optionDiv);
56 |
57 | return slider;
58 | }
59 | };
60 |
61 | });
62 |
--------------------------------------------------------------------------------
/js/commonUtils/utilities.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Kate Compton
3 | */
4 |
5 | define([], function() {
6 | function toShort(s) {
7 | if (s.length < 20)
8 | return s;
9 | else
10 | return s.substring(0, 17) + "...";
11 | }
12 |
13 | var entityMap = {
14 | "&" : "&",
15 | "<" : "<",
16 | ">" : ">",
17 | '"' : '"',
18 | "'" : ''',
19 | "/" : '/'
20 | };
21 |
22 | function escapeHtml(string) {
23 | return String(string).replace(/[&<>"'\/]/g, function(s) {
24 | return entityMap[s];
25 | });
26 | }
27 |
28 | var randomCap = function(array) {
29 | return utilities.capitaliseFirstLetter(utilities.getRandom(array));
30 | };
31 |
32 | var utilities = {
33 |
34 | // courtesy of http://www.zacwitte.com/javascript-sigmoid-function
35 | sigmoid : function(t, min, max) {
36 | // [0, 1]
37 | var v = 1 / (1 + Math.pow(Math.E, -t));
38 | if (min !== undefined) {
39 | v = v * (max - min) + min;
40 | }
41 | return v;
42 | },
43 |
44 |
45 |
46 | remove : function(list, item) {
47 |
48 | var index = list.indexOf(item);
49 |
50 | if (index > -1) {
51 | list.splice(index, 1);
52 | }
53 | },
54 |
55 | splitSections : function(s, open, close, splitter) {
56 |
57 | var levels = this.splitParens(s, open, close);
58 | var sections = [];
59 | for (var i = 0; i < levels.outer.length; i++) {
60 |
61 | // add the outer's split
62 |
63 | if (levels.outer[i].length > 0) {
64 | // console.log("outer:" + toShort(levels.outer[i]));
65 |
66 | var split = levels.outer[i].split(splitter);
67 | // Odds are in quotes, evens are in
68 |
69 | if (split.length % 2 !== 1) {
70 | console.log(split);
71 | throw ("Uneven number of splitters in section: " + levels.outer[i]);
72 |
73 | }
74 |
75 | for (var j = 0; j < split.length; j++) {
76 | var type = "in";
77 | if (j % 2 === 0)
78 | type = "out";
79 |
80 | sections.push({
81 | type : type,
82 | text : split[j]
83 | });
84 |
85 | }
86 | }
87 |
88 | // Add the inner
89 | if (i < levels.inner.length) {
90 | sections.push({
91 | type : "hidden",
92 | text : levels.inner[i]
93 | });
94 | }
95 |
96 | }
97 | return sections;
98 | },
99 |
100 | splitParens : function(s, open, close) {
101 | var level = 0;
102 | var outer = [];
103 | var inner = [];
104 | var start = 0;
105 | for (var i = 0; i < s.length; i++) {
106 | var c = s.charAt(i);
107 | if (level === 0) {
108 | if (c === open) {
109 | var section = s.substring(start, i);
110 | outer.push(section);
111 | start = i + 1;
112 | level++;
113 | } else {
114 |
115 | }
116 |
117 | } else {
118 | if (c === close) {
119 | level--;
120 | if (level === 0) {
121 | var section = s.substring(start, i);
122 | inner.push(section);
123 | start = i + 1;
124 | }
125 | }
126 |
127 | if (c === open) {
128 | level++;
129 | }
130 | }
131 |
132 | }
133 |
134 | outer.push(s.substring(start, i));
135 | return {
136 | inner : inner,
137 | outer : outer
138 | };
139 | },
140 |
141 | isDuplicate : function(a, b, ignoreKeys) {
142 |
143 | var joint = {};
144 | $.extend(joint, a);
145 | $.extend(joint, b);
146 |
147 | var keys = Object.keys(joint);
148 | var comparisons = 0;
149 | var differences = [];
150 | var similarities = [];
151 | for (var i = 0; i < keys.length; i++) {
152 |
153 | var key = keys[i];
154 | if (!$.inArray(key, ignoreKeys)) {
155 | comparisons++;
156 |
157 | if (a[key] !== b[key]) {
158 | differences.push(key);
159 | } else {
160 | similarities.push(key + ": " + b[key]);
161 | }
162 | } else {
163 |
164 | }
165 | }
166 |
167 | var pct = differences.length / comparisons;
168 | if (pct === 1)
169 | return false;
170 |
171 | if (pct === 0) {
172 | return true;
173 | }
174 |
175 | if (pct < .9) {
176 | console.log(a.id + " is similar to " + b.id);
177 | console.log(" differences: " + differences);
178 | console.log(" similarities: " + similarities);
179 | }
180 | return pct < .5;
181 | },
182 |
183 | escapeHTML : escapeHtml,
184 |
185 | // put noise in here too?
186 | capitaliseFirstLetter : function(string) {
187 | return string.charAt(0).toUpperCase() + string.slice(1);
188 | },
189 | lowerCaseFirstLetter : function(string) {
190 | return string.charAt(0).toLowerCase() + string.slice(1);
191 | },
192 | loremIpsum : function(length) {
193 | var start = Math.floor((Math.random() * (this.loremIpsumSource.length - length)));
194 | return this.loremIpsumSource.substring(start, start + length);
195 | },
196 | loremIpsumSentences : function() {
197 | var s = utilities.getRandom(this.sentences) + ".";
198 | /*
199 | var count = 0;
200 | while (Math.random() > .6 && count < 3) {
201 | // if (Math.random() > .5)
202 | s += "
";
203 |
204 | count++;
205 | s += " " + utilities.getRandom(this.sentences) + ".";
206 | }
207 | */
208 | return s;
209 | },
210 | words : {
211 | syllables : {
212 | first : "B C D F G H J K L M N P Qu R S T V W X Y Z St Fl Bl Pr Kr Ll Chr Sk Br Sth Ch Dhr Dr Sl Sc Sh Thl Thr Pl Fr Phr Phl Wh".split(" "),
213 | middle : "an all ar art air aean af av ant app ab er en eor eon ent enth irt ian ion iont ill il ipp in is it ik ob ov orb oon ion uk uf un ull urk".split(" "),
214 | composites : "estr antr okl ackl".split(" "),
215 | last : "a ia ea u y en am is on an o io i el ios ius ian ean ekang anth ".split(" "),
216 | },
217 | animals : "amoeba mongoose capybara yeti dragon unicorn sphinx kangaroo boa nematode sheep quail goat corgi agouti zebra giraffe rhino skunk dolphin whale bullfrog okapi sloth monkey orangutan grizzly moose elk dikdik ibis stork finch nightingale goose robin eagle hawk iguana tortoise panther lion tiger gnu reindeer raccoon opossum".split(" "),
218 | moods : "angry bemused elated skeptical morose gleeful curious sleepy hopeful ashamed alert energetic exhausted giddy grateful groggy grumpy irate jealous jubilant lethargic sated lonely relaxed restless surprised tired thankful".split(" "),
219 | colors : "ivory white silver ecru scarlet red burgundy ruby crimson carnelian pink rose grey pewter charcoal slate onyx black mahogany brown green emerald blue sapphire turquoise aquamarine teal gold yellow carnation orange lavender purple magenta lilac ebony amethyst garnet".split(" "),
220 | adventures : "lament story epic tears wish desire dance mystery enigma drama path training sorrows joy tragedy comedy riddle puzzle regret victory loss song adventure question quest vow oath tale travels".split(" "),
221 | personTitle : "Dr. Ms. Rev. St. Mr Professor".split(" "),
222 | personName : "Nara Alphonse Miranda Gertrude Zhangwei Lichiang LiJing Aiysha Alesandro Enrik Seo-yun Ji-woo Oisha Tarik Antoni Tereza Freja Ionnis Lukas Lorenzo Zhangyan Shu-hui Mei-ling Youssef Althea Gabriel Valentina Hala Noor Omar Sai Pranav Mikhail Ezekiel Sebastian Ibrahim Fatma Mariam Isabel Loretta Phoebe Nigel Amaya Maia Zelda".split(" "),
223 | getRandomPhrase : function() {
224 | return utilities.getRandom(utilities.words.moods) + " " + utilities.getRandom(utilities.words.colors) + " " + utilities.getRandom(utilities.words.animals);
225 | },
226 | getRandomName : function() {
227 | var s = utilities.getRandom(this.personName) + " " + this.getRandomWord();
228 | if (Math.random() > .5)
229 | s = utilities.getRandom(this.personTitle) + " " + s;
230 | return s;
231 | },
232 | getRandomTitle : function() {
233 | var adj = randomCap(this.moods);
234 | if (Math.random() > .5)
235 | adj = randomCap(this.colors);
236 | return "The " + randomCap(this.adventures) + " of the " + adj + " " + randomCap(this.animals);
237 | },
238 | getRandomWord : function(lengthMult) {
239 | if (!lengthMult)
240 | lengthMult = 1;
241 | var s = utilities.getRandom(this.syllables.first);
242 | if (Math.random() < .4)
243 | s = utilities.capitaliseFirstLetter(utilities.getRandom(this.syllables.middle));
244 |
245 | var count = Math.floor(Math.random() * lengthMult * 3);
246 | for (var i = 0; i < count; i++) {
247 | var mid = utilities.getRandom(this.syllables.middle);
248 | s += mid;
249 |
250 | }
251 | s += utilities.getRandom(this.syllables.last);
252 |
253 | if (s.length > 6 * lengthMult && Math.random < .8)
254 | s = utilities.words.getRandomWord();
255 | if (s.length > 9 * lengthMult && Math.random < .9)
256 | s = utilities.words.getRandomWord();
257 |
258 | if (s.length < 6 * lengthMult && Math.random() < .2)
259 | s += "-" + utilities.words.getRandomWord();
260 | else if (s.length < 6 * lengthMult && Math.random() < .2)
261 | s += "'" + utilities.getRandom(this.syllables.last);
262 |
263 | return s;
264 | }
265 | },
266 | arrayToString : function(array) {
267 | s = "";
268 | $.each(array, function(index, obj) {
269 | if (index !== 0)
270 | s += ", ";
271 | s += obj;
272 | });
273 | return s;
274 | },
275 | inSquareBrackets : function(s) {
276 | return "[" + s + "]";
277 | },
278 |
279 | join : function(array, spacer, f) {
280 | var s = "";
281 | for (var i = 0; i < array.length; i++) {
282 | if (s.length === 0)
283 | s += f(array[i]);
284 | else
285 | s += spacer + f(array[i]);
286 | }
287 | return s;
288 | },
289 |
290 | spaceTo : function(txt, length) {
291 | var label = txt;
292 | label = label.substring(0, length - 1);
293 | if (length) {
294 |
295 | label = label + utilities.getSpacer(length - label.length);
296 | }
297 | return label;
298 | },
299 |
300 | getSpacer : function(count) {
301 | var s = "";
302 | for (var i = 0; i < count; i++) {
303 | s += " ";
304 | }
305 | return s;
306 | },
307 | sCurve : function(v, iterations) {
308 | if (iterations === undefined)
309 | iterations = 1;
310 | for (var i = 0; i < iterations; i++) {
311 | var v2 = .5 - .5 * Math.cos(v * Math.PI);
312 | v = v2;
313 | }
314 | return v;
315 | },
316 | within : function(val, min, max) {
317 | return (val >= min) && (val <= max);
318 | },
319 |
320 | // Inefficient, fix someday
321 | // the weight is determined by the function getWeight(index, item, list)
322 | getWeightedRandomIndex : function(array) {
323 | var totalWeight = 0;
324 | var length = array.length;
325 |
326 | for (var i = 0; i < length; i++) {
327 |
328 | totalWeight += array[i];
329 | };
330 |
331 | var target = Math.random() * totalWeight;
332 | var cumWeight = 0;
333 |
334 | for (var i = 0; i < length; i++) {
335 | cumWeight += array[i];
336 |
337 | if (target <= cumWeight) {
338 | return i;
339 | }
340 |
341 | };
342 |
343 | },
344 |
345 | // Get a random, from an array
346 | getRandom : function(array, power) {
347 | if (power)
348 | return array[Math.floor(Math.pow(Math.random(), power) * array.length)];
349 | else
350 | return array[Math.floor(Math.random() * array.length)];
351 | },
352 | getRandomIndex : function(array) {
353 | return Math.floor(Math.random() * Math.round(array.length - 1));
354 | },
355 | getRandomKey : function(obj) {
356 | return this.getRandom(Object.keys(obj));
357 | },
358 | constrain : function(val, lowerBound, upperBound) {
359 | if (Math.max(val, upperBound) === val)
360 | return upperBound;
361 | if (Math.min(val, lowerBound) === val)
362 | return lowerBound;
363 | return val;
364 | },
365 | lerp : function(start, end, percent) {
366 | return (start + percent * (end - start));
367 | },
368 | lerpAngles : function(start, end, pct) {
369 | var dTheta = end - start;
370 | },
371 |
372 | // angle between 0 and 2 PI
373 | normalizeAngle : function(theta) {
374 | var twopi = Math.PI * 2;
375 | theta = (((theta % twopi) + twopi) % twopi);
376 | return theta;
377 | },
378 |
379 | // Rertun a random, possible between two numbers
380 | random : function() {
381 | if (arguments.length === 0)
382 | return Math.random();
383 | if (arguments.length === 1)
384 | return Math.random() * arguments[i];
385 | if (arguments.length === 2)
386 | return Math.random() * (arguments[1] - arguments[0]) + arguments[0];
387 |
388 | return Math.random();
389 | },
390 | roundNumber : function(num, places) {
391 | // default 2 decimal places
392 | if (places === undefined) {
393 | return parseFloat(Math.round(num * 100) / 100).toFixed(2);
394 | } else {
395 | return parseFloat(Math.round(num * 100) / 100).toFixed(places);
396 | }
397 | },
398 | angleBetween : function(a, b) {
399 | var dTheta = b - a;
400 | dTheta = ((dTheta % (Math.PI * 2)) + Math.PI * 2) % (Math.PI * 2);
401 | if (dTheta > Math.PI)
402 | dTheta -= Math.PI * 2;
403 | return dTheta;
404 | },
405 | isString : function(s) {
406 | return ( typeof s == 'string' || s instanceof String);
407 | }
408 | };
409 |
410 | utilities.loremIpsumSource = "PREFACE WHY is it that a little spice of deviltry lends not an unpleasantly titillating twang to the great mass of respectable flour that goes to make up the pudding of our modern civilization? And pertinent to this question another--Why is it that the pirate has, and always has had, a certain lurid glamour of the heroical enveloping him round about? Is there, deep under the accumulated debris of culture, a hidden groundwork of the old-time savage? Is there even in these well-regulated times an unsubdued nature in the respectable mental household of every one of us that still kicks against the pricks of law and order? To make my meaning more clear, would not every boy, for instance--that is, every boy of any account--rather be a pirate captain than a Member of Parliament? And we ourselves--would we not rather read such a story as that of Captain Avery's capture of the East Indian treasure ship, with its beautiful princess and load of jewels (which gems he sold by the handful, history sayeth, to a Bristol merchant), than, say, one of Bishop Atterbury's sermons, or the goodly Master Robert Boyle's religious romance of 'Theodora and Didymus'? It is to be apprehended that to the unregenerate nature of most of us there can be but one answer to such a query. In the pleasurable warmth the heart feels in answer to tales of derring-do Nelson's battles are all mightily interesting, but, even in spite of their romance of splendid courage, I fancy that the majority of us would rather turn back over the leaves of history to read how Drake captured the Spanish treasure ship in the South Sea, and of how he divided such a quantity of booty in the Island of Plate (so named because of the tremendous dividend there declared) that it had to be measured in quart bowls, being too considerable to be counted. Courage and daring, no matter how mad and ungodly, have always a redundancy of vim and life to recommend them to the nether man that lies within us, and no doubt his desperate courage, his battle against the tremendous odds of all the civilized world of law and order, have had much to do in making a popular hero of our friend of the black flag. But it is not altogether courage and daring that endear him to our hearts. There is another and perhaps a greater kinship in that lust for wealth that makes one's fancy revel more pleasantly in the story of the division of treasure in the pirate's island retreat, the hiding of his godless gains somewhere in the sandy stretch of tropic beach, there to remain hidden until the time should come to rake the doubloons up again and to spend them like a lord in polite society, than in the most thrilling tales of his wonderful escapes from commissioned cruisers through tortuous channels between the coral reefs. And what a life of adventure is his, to be sure! A life of constant alertness, constant danger, constant escape! An ocean Ishmaelite, he wanders forever aimlessly, homelessly; now unheard of for months, now careening his boat on some lonely uninhabited shore, now appearing suddenly to swoop down on some merchant vessel with rattle of musketry, shouting, yells, and a hell of unbridled passions let loose to rend and tear. What a Carlislean hero! What a setting of blood and lust and flame and rapine for such a hero! Piracy, such as was practiced in the flower of its days--that is, during the early eighteenth century--was no sudden growth. It was an evolution, from the semi-lawful buccaneering of the sixteenth century, just as buccaneering was upon its part, in a certain sense, an evolution from the unorganized, unauthorized warfare of the Tudor period. For there was a deal of piratical smack in the anti-Spanish ventures of Elizabethan days. Many of the adventurers--of the Sir Francis Drake school, for instance--actually overstepped again and again the bounds of international law, entering into the realms of de facto piracy. Nevertheless, while their doings were not recognized officially by the government, the perpetrators were neither punished nor reprimanded for their excursions against Spanish commerce at home or in the West Indies; rather were they commended, and it was considered not altogether a discreditable thing for men to get rich upon the spoils taken from Spanish galleons in times of nominal peace. Many of the most reputable citizens and merchants of London, when they felt that the queen failed in her duty of pushing the fight against the great Catholic Power, fitted out fleets upon their own account and sent them to levy good Protestant war of a private nature upon the Pope's anointed. Some of the treasures captured in such ventures were immense, stupendous, unbelievable. For an example, one can hardly credit the truth of the 'purchase' gained by Drake in the famous capture of the plate ship in the South Sea. One of the old buccaneer writers of a century later says: 'The Spaniards affirm to this day that he took at that time twelvescore tons of plate and sixteen bowls of coined money a man (his number being then forty-five men in all), insomuch that they were forced to heave much of it overboard, because his ship could not carry it all.' Maybe this was a very greatly exaggerated statement put by the author and his Spanish authorities, nevertheless there was enough truth in it to prove very conclusively to the bold minds of the age that tremendous profits--'purchases' they called them--were to be made from piracy. The Western World is filled with the names of daring mariners of those old days, who came flitting across the great trackless ocean in their little tublike boats of a few hundred tons burden, partly to explore unknown seas, partly--largely, perhaps--in pursuit of Spanish treasure: Frobisher, Davis, Drake, and a score of others. In this left-handed war against Catholic Spain many of the adventurers were, no doubt, stirred and incited by a grim, Calvinistic, puritanical zeal for Protestantism. But equally beyond doubt the gold and silver and plate of the 'Scarlet Woman' had much to do with the persistent energy with which these hardy mariners braved the mysterious, unknown terrors of the great unknown ocean that stretched away to the sunset, there in faraway waters to attack the huge, unwieldy, treasure-laden galleons that sailed up and down the Caribbean Sea and through the Bahama Channel. Of all ghastly and terrible things old-time religious war was the most ghastly and terrible. One can hardly credit nowadays the cold, callous cruelty of those times. Generally death was the least penalty that capture entailed. When the Spaniards made prisoners of the English, the Inquisition took them in hand, and what that meant all the world knows. When the English captured a Spanish vessel the prisoners were tortured, either for the sake of revenge or to compel them to disclose where treasure lay hidden. Cruelty begat cruelty, and it would be hard to say whether the Anglo-Saxon or the Latin showed himself to be most proficient in torturing his victim. When Cobham, for instance, captured the Spanish ship in the Bay of Biscay, after all resistance was over and the heat of the battle had cooled, he ordered his crew to bind the captain and all of the crew and every Spaniard aboard--whether in arms or not--to sew them up in the mainsail and to fling them overboard. There were some twenty dead bodies in the sail when a few days later it was washed up on the shore. Of course such acts were not likely to go unavenged, and many an innocent life was sacrificed to pay the debt of Cobham's cruelty. Nothing could be more piratical than all this. Nevertheless, as was said, it was winked at, condoned, if not sanctioned, by the law; and it was not beneath people of family and respectability to take part in it. But by and by Protestantism and Catholicism began to be at somewhat less deadly enmity with each other; religious wars were still far enough from being ended, but the scabbard of the sword was no longer flung away when the blade was drawn. And so followed a time of nominal peace, and a generation arose with whom it was no longer respectable and worthy--one might say a matter of duty--to fight a country with which one's own land was not at war. Nevertheless, the seed had been sown; it had been demonstrated that it was feasible to practice piracy against Spain and not to suffer therefor. Blood had been shed and cruelty practiced, and, once indulged, no lust seems stronger than that of shedding blood and practicing cruelty. Though Spain might be ever so well grounded in peace at home, in the West Indies she was always at war with the whole world--English, French, Dutch. It was almost a matter of life or death with her to keep her hold upon the New World. At home she was bankrupt and, upon the earthquake of the Reformation, her power was already beginning to totter and to crumble to pieces. America was her treasure house, and from it alone could she hope to keep her leaking purse full of gold and silver. So it was that she strove strenuously, desperately, to keep out the world from her American possessions--a bootless task, for the old order upon which her power rested was broken and crumbled forever. But still she strove, fighting against fate, and so it was that in the tropical America it was one continual war between her and all the world. Thus it came that, long after piracy ceased to be allowed at home, it continued in those far-away seas with unabated vigor, recruiting to its service all that lawless malign element which gathers together in every newly opened country where the only law is lawlessness, where might is right and where a living is to be gained with no more trouble than cutting a throat. {signature Howard Pyle His Mark} HOWARD PILE'S BOOK OF PIRATES Chapter I. BUCCANEERS AND MAROONERS OF THE SPANISH MAIN JUST above the northwestern shore of the old island of Hispaniola--the Santo Domingo of our day--and separated from it only by a narrow channel of some five or six miles in width, lies a queer little hunch of an island, known, because of a distant resemblance to that animal, as the Tortuga de Mar, or sea turtle. It is not more than twenty miles in length by perhaps seven or eight in breadth; it is only a little spot of land, and as you look at it upon the map a pin's head would almost cover it; yet from that spot, as from a center of inflammation, a burning fire of human wickedness and ruthlessness and lust overran the world, and spread terror and death throughout the Spanish West Indies, from St. Augustine to the island of Trinidad, and from Panama to the coasts of Peru. About the middle of the seventeenth century certain French adventurers set out from the fortified island of St. Christopher in longboats and hoys, directing their course to the westward, there to discover new islands. Sighting Hispaniola 'with abundance of joy,' they landed, and went into the country, where they found great quantities of wild cattle, horses, and swine. Now vessels on the return voyage to Europe from the West Indies needed revictualing, and food, especially flesh, was at a premium in the islands of the Spanish Main; wherefore a great profit was to be turned in preserving beef and pork, and selling the flesh to homeward-bound vessels. The northwestern shore of Hispaniola, lying as it does at the eastern outlet of the old Bahama Channel, running between the island of Cuba and the great Bahama Banks, lay almost in the very main stream of travel. The pioneer Frenchmen were not slow to discover the double advantage to be reaped from the wild cattle that cost them nothing to procure, and a market for the flesh ready found for them. So down upon Hispaniola they came by boatloads and shiploads, gathering like a swarm of mosquitoes, and overrunning the whole western end of the island. There they established themselves, spending the time alternately in hunting the wild cattle and buccanning(1) the meat, and squandering their hardly earned gains in wild debauchery, the opportunities for which were never lacking in the Spanish West Indies.";
411 | utilities.sentences = utilities.loremIpsumSource.split(".");
412 | return utilities;
413 | });
414 |
415 |
--------------------------------------------------------------------------------
/js/commonUtils/vector.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Kate Compton
3 | */
4 |
5 | // Reusable Vector class
6 |
7 | define(["inheritance"], function(Inheritance) {
8 | // convert angle to -PI, PI
9 | var normalizeAngle = function(angle) {
10 | angle = angle % (Math.PI * 2);
11 | if (angle > Math.PI)
12 | angle -= Math.PI * 2;
13 | return angle;
14 | };
15 |
16 | Vector = Class.extend({
17 |
18 | init : function(x, y, z) {
19 | // actually another vector, clone it
20 | if (x === undefined) {
21 | this.x = 0;
22 | this.y = 0;
23 | this.z = 0;
24 | } else {
25 | if (x.x !== undefined) {
26 | this.x = x.x;
27 | this.y = x.y;
28 | this.z = x.z;
29 | } else {
30 | this.x = x;
31 | this.y = y;
32 |
33 | this.z = 0;
34 | if (z !== undefined)
35 | this.z = z;
36 |
37 | }
38 | }
39 |
40 | if (!this.isValid())
41 | throw new Error(this.invalidToString() + " is not a valid vector");
42 | },
43 |
44 | clone : function() {
45 | return new Vector(this);
46 | },
47 |
48 | cloneInto : function(v) {
49 | v.x = this.x;
50 | v.y = this.y;
51 | v.z = this.z;
52 |
53 | },
54 |
55 | addMultiple : function(v, m) {
56 | this.x += v.x * m;
57 | this.y += v.y * m;
58 | this.z += v.z * m;
59 | },
60 | addPolar : function(r, theta) {
61 | this.x += r * Math.cos(theta);
62 | this.y += r * Math.sin(theta);
63 | },
64 |
65 | addSpherical : function(r, theta, phi) {
66 | this.x += r * Math.cos(theta) * Math.cos(phi);
67 | this.y += r * Math.sin(theta) * Math.cos(phi);
68 | this.z += r * Math.sin(phi);
69 | },
70 |
71 | addRotated : function(v, theta) {
72 | var cs = Math.cos(theta);
73 | var sn = Math.sin(theta);
74 | var x = v.x * cs - v.y * sn;
75 | var y = v.x * sn + v.y * cs;
76 | this.x += x;
77 | this.y += y;
78 | return this;
79 | },
80 |
81 | setToAverage : function(array) {
82 | if (array.length === 0) {
83 | this.setTo(0, 0);
84 | } else {
85 | this.mult(0);
86 | for (var i = 0; i < array.length; i++) {
87 | this.add(array[i]);
88 | }
89 | this.div(array.length);
90 | }
91 | },
92 |
93 | setToPolar : function(r, theta) {
94 | this.x = r * Math.cos(theta);
95 | this.y = r * Math.sin(theta);
96 | return this;
97 | },
98 | setToCylindrical : function(r, theta, z) {
99 | this.x = r * Math.cos(theta);
100 | this.y = r * Math.sin(theta);
101 | this.z = z;
102 | return this;
103 | },
104 | setToPolarOffset : function(v, r, theta) {
105 | this.x = v.x + r * Math.cos(theta);
106 | this.y = v.y + r * Math.sin(theta);
107 | this.z = v.z;
108 | return this;
109 | },
110 | setToMultiple : function(v, m) {
111 | this.x = v.x * m;
112 | this.y = v.y * m;
113 | this.z = v.z * m;
114 | return this;
115 | },
116 | setToLerp : function(v0, v1, m) {
117 | var m1 = 1 - m;
118 | this.x = v0.x * m1 + v1.x * m;
119 | this.y = v0.y * m1 + v1.y * m;
120 | this.z = v0.z * m1 + v1.z * m;
121 | return this;
122 | },
123 |
124 | setToAddMultiple : function(v0, m0, v1, m1) {
125 | this.x = v0.x * m0 + v1.x * m1;
126 | this.y = v0.y * m0 + v1.y * m1;
127 | this.z = v0.z * m0 + v1.z * m1;
128 | return this;
129 | },
130 |
131 | setToAdd : function(v0, v1) {
132 | this.x = v0.x + v1.x;
133 | this.y = v0.y + v1.y;
134 | this.z = v0.z + v1.z;
135 |
136 | return this;
137 | },
138 | setToAddMultiple : function(v0, v1, m) {
139 | this.x = v0.x + v1.x * m;
140 | this.y = v0.y + v1.y * m;
141 | this.z = v0.z + v1.z * m;
142 |
143 | return this;
144 | },
145 |
146 | setToDifference : function(v0, v1) {
147 | this.x = v0.x - v1.x;
148 | this.y = v0.y - v1.y;
149 | this.z = v0.z - v1.z;
150 | return this;
151 | },
152 |
153 | setTo : function(x, y, z) {
154 | // Just in case this was passed a vector
155 | if (x.x !== undefined) {
156 | this.x = x.x;
157 | this.y = x.y;
158 | this.z = x.z;
159 | if (this.z === undefined)
160 | this.z = 0;
161 |
162 | } else {
163 | this.x = x;
164 | this.y = y;
165 | if (z !== undefined)
166 | this.z = z;
167 | }
168 | if (!this.isValid())
169 | throw new Error(this.invalidToString() + " is not a valid vector");
170 |
171 | return this;
172 | },
173 |
174 | magnitude : function() {
175 | return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
176 | },
177 |
178 | normalize : function() {
179 | this.div(this.magnitude());
180 | return this;
181 |
182 | },
183 |
184 | constrainMagnitude : function(min, max) {
185 | var d = this.magnitude();
186 | if (d !== 0) {
187 | var d2 = utilities.constrain(d, min, max);
188 | this.mult(d2 / d);
189 | }
190 | },
191 |
192 | getDistanceTo : function(p) {
193 | var dx = this.x - p.x;
194 | var dy = this.y - p.y;
195 | var dz = this.z - p.z;
196 | return Math.sqrt(dx * dx + dy * dy + dz * dz);
197 | },
198 |
199 | getDistanceToIgnoreZ : function(p) {
200 | var dx = this.x - p.x;
201 | var dy = this.y - p.y;
202 |
203 | return Math.sqrt(dx * dx + dy * dy);
204 | },
205 |
206 | getAngleTo : function(p) {
207 | var dx = p.x - this.x;
208 | var dy = p.y - this.y;
209 | //var dz = this.z - p.z;
210 | return Math.atan2(dy, dx);
211 | },
212 |
213 | getNormalTo : function(p) {
214 | var offset = this.getOffsetTo(p);
215 | offset.normalize();
216 | var temp = offset.x;
217 | offset.x = offset.y;
218 | offset.y = -temp;
219 | return offset;
220 | },
221 |
222 | //===========================================================
223 | //===========================================================
224 | // Complex geometry
225 |
226 | dot : function(v) {
227 | return v.x * this.x + v.y * this.y + v.z * this.z;
228 | },
229 | cross : function(v) {
230 | return new Vector(this.y * v.z - this.z * v.y, this.z * v.x - this.x * v.z, this.x * v.y - this.y * v.x);
231 | },
232 |
233 | getAngleBetween : function(v) {
234 | return Math.acos(this.dot(v) / (this.magnitude() * v.magnitude()));
235 | },
236 |
237 | getCrossAngleBetween : function(v) {
238 | var cross = this.cross(v);
239 | if (cross.z > 0)
240 | return -Math.asin(cross.magnitude() / (this.magnitude() * v.magnitude()));
241 | else
242 | return Math.asin(cross.magnitude() / (this.magnitude() * v.magnitude()));
243 | },
244 |
245 | getNormalizedAngleBetween : function(v) {
246 | var theta0 = this.getAngle();
247 | var theta1 = v.getAngle();
248 | return normalizeAngle(theta1 - theta0);
249 | },
250 |
251 | isInTriangle : function(triangle) {
252 |
253 | //credit: http://www.blackpawn.com/texts/pointinpoly/default.html
254 | var ax = triangle[0].x;
255 | var ay = triangle[0].y;
256 | var bx = triangle[1].x;
257 | var by = triangle[1].y;
258 | var cx = triangle[2].x;
259 | var cy = triangle[2].y;
260 |
261 | var v0 = [cx - ax, cy - ay];
262 | var v1 = [bx - ax, by - ay];
263 | var v2 = [this.x - ax, this.y - ay];
264 |
265 | var dot00 = (v0[0] * v0[0]) + (v0[1] * v0[1]);
266 | var dot01 = (v0[0] * v1[0]) + (v0[1] * v1[1]);
267 | var dot02 = (v0[0] * v2[0]) + (v0[1] * v2[1]);
268 | var dot11 = (v1[0] * v1[0]) + (v1[1] * v1[1]);
269 | var dot12 = (v1[0] * v2[0]) + (v1[1] * v2[1]);
270 |
271 | var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
272 |
273 | var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
274 | var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
275 |
276 | return ((u >= 0) && (v >= 0) && (u + v < 1));
277 |
278 | },
279 |
280 | isInPolygon : function(poly) {
281 | var pt = this;
282 | for (var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
283 | ((poly[i].y <= pt.y && pt.y < poly[j].y) || (poly[j].y <= pt.y && pt.y < poly[i].y)) && (pt.x < (poly[j].x - poly[i].x) * (pt.y - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x) && ( c = !c);
284 | return c;
285 | },
286 |
287 | //===========================================================
288 | //===========================================================
289 | // Add and sub and mult and div functions
290 |
291 | add : function(x, y, z) {
292 | if (y !== undefined) {
293 | this.x += x;
294 | this.y += y;
295 | if (z !== undefined)
296 | this.z += z;
297 | } else {
298 | this.x += x.x;
299 | this.y += x.y;
300 | this.z += x.z;
301 | }
302 | },
303 |
304 | sub : function(v) {
305 | this.x -= v.x;
306 | this.y -= v.y;
307 | this.z -= v.z;
308 | },
309 | mult : function(m) {
310 | this.x *= m;
311 | this.y *= m;
312 | this.z *= m;
313 | },
314 | div : function(m) {
315 | this.x /= m;
316 | this.y /= m;
317 | this.z /= m;
318 | },
319 |
320 | getOffsetTo : function(v) {
321 | return new Vector(v.x - this.x, v.y - this.y, v.z - this.z);
322 | },
323 |
324 | getAngle : function() {
325 | return Math.atan2(this.y, this.x);
326 | },
327 |
328 | rotate : function(theta) {
329 | var cs = Math.cos(theta);
330 | var sn = Math.sin(theta);
331 | var x = this.x * cs - this.y * sn;
332 | var y = this.x * sn + this.y * cs;
333 | this.x = x;
334 | this.y = y;
335 | },
336 |
337 | //===========================================================
338 | //===========================================================
339 |
340 | // Lerp a vector!
341 | lerp : function(otherVector, percent) {
342 | var lerpVect = new Vector(utilities.lerp(this.x, otherVector.x, percent), utilities.lerp(this.y, otherVector.y, percent), utilities.lerp(this.z, otherVector.z, percent));
343 | return lerpVect;
344 | },
345 |
346 | //===========================================================
347 | //===========================================================
348 | isValid : function() {
349 | var hasNaN = isNaN(this.x) || isNaN(this.y) || isNaN(this.z);
350 | var hasUndefined = this.x === undefined || this.y === undefined || this.z === undefined;
351 | var hasInfinity = Math.abs(this.x) === Infinity || Math.abs(this.y) === Infinity || Math.abs(this.z) === Infinity;
352 |
353 | var valid = !(hasNaN || hasUndefined || hasInfinity);
354 | // if (!valid)
355 | // console.log(hasNaN + " " + hasUndefined + " " + hasInfinity);
356 | return valid;
357 | },
358 |
359 | //===========================================================
360 | //===========================================================
361 | translateTo : function(g) {
362 | g.translate(this.x, this.y);
363 | },
364 |
365 | //===========================================================
366 | //===========================================================
367 |
368 | bezier : function(g, c0, c1) {
369 | g.bezierVertex(c0.x, c0.y, c1.x, c1.y, this.x, this.y);
370 | },
371 |
372 | bezierTo : function(g, c0, c1, p) {
373 | g.bezier(this.x, this.y, c0.x, c0.y, c1.x, c1.y, p.x, p.y);
374 | },
375 | bezierWithRelativeControlPoints : function(g, p, c0, c1) {
376 | // "x" and "y" were not defined, so I added "this." in front. Hopefully that's the intended action (April)
377 | g.bezierVertex(p.x + c0.x, p.y + c0.y, this.x + c1.x, this.y + c1.y, this.x, this.y);
378 | },
379 |
380 | setToBezierPoint : function(p0, c0, c1, p1, t0) {
381 | // (1-t)^3 P_0 + 3(1-t)^2t P_1 + 3(1-t)t^2 P_2 + t^3 P_3
382 | var t1 = 1 - t0;
383 | this.setTo(0, 0, 0);
384 | this.addMultiple(p0, t1 * t1 * t1);
385 | this.addMultiple(c0, 3 * t1 * t1 * t0);
386 | this.addMultiple(c1, 3 * t1 * t0 * t0);
387 | this.addMultiple(p1, t0 * t0 * t0);
388 | },
389 |
390 | vertex : function(g) {
391 | g.vertex(this.x, this.y);
392 | },
393 |
394 | offsetVertex : function(g, offset, m) {
395 | if (m === undefined)
396 | m = 1;
397 | g.vertex(this.x + offset.x * m, this.y + offset.y * m);
398 | },
399 |
400 | draw : function(g) {
401 | var radius = 5;
402 | g.ellipse(this.x, this.y, radius, radius);
403 | },
404 | drawCircle : function(g, radius, radius2) {
405 | if (radius2 === undefined)
406 | radius2 = radius;
407 | g.ellipse(this.x, this.y, radius, radius2);
408 | },
409 |
410 | drawScreenCircle : function(g, camera, radius, radius2) {
411 | this.setScreenPos(camera);
412 | this.screenPos.drawCircle(g, radius, radius2);
413 | },
414 |
415 | drawOffsetCircle : function(g, offset, radius) {
416 | g.ellipse(this.x + offset.x, this.y + offset.y, radius, radius);
417 | },
418 |
419 | drawLineTo : function(g, v, offset, inset0, inset1) {
420 | if (offset) {
421 | var n = this.getNormalTo(v);
422 | var m = this.getOffsetTo(v);
423 | m.normalize();
424 | n.mult(offset);
425 |
426 | g.line(this.x + n.x + m.x * inset0, this.y + n.y + m.y * inset0, v.x + n.x + -m.x * inset1, v.y + n.y + -m.y * inset1);
427 | } else
428 | g.line(this.x, this.y, v.x, v.y);
429 | },
430 |
431 | drawOffsetLineTo : function(g, v, m, offset) {
432 | var mx = m * offset.x;
433 | var my = m * offset.y;
434 |
435 | g.line(this.x + mx, this.y + my, v.x + mx, v.y + my);
436 | },
437 |
438 | drawLerpedLineTo : function(g, v, startLerp, endLerp) {
439 | var dx = v.x - this.x;
440 | var dy = v.y - this.y;
441 |
442 | g.line(this.x + dx * startLerp, this.y + dy * startLerp, this.x + dx * endLerp, this.y + dy * endLerp);
443 | },
444 |
445 | drawArrow : function(g, v, m, headSize, endSpace) {
446 |
447 | if (headSize) {
448 | g.pushMatrix();
449 | g.translate(this.x + v.x * m, this.y + v.y * m);
450 | g.rotate(v.getAngle());
451 |
452 | endSpace = (endSpace === undefined) ? 2 : endSpace;
453 |
454 | g.line(-(v.magnitude() * m - endSpace), 0, -endSpace, 0);
455 | g.noStroke();
456 |
457 | g.beginShape();
458 | g.vertex(-headSize, 0);
459 | g.vertex(-headSize, headSize * .4);
460 | g.vertex(0, 0);
461 | g.vertex(-headSize, -headSize * .4);
462 | g.endShape();
463 | g.popMatrix();
464 | } else {
465 | g.line(this.x, this.y, v.x * m + this.x, v.y * m + this.y);
466 |
467 | }
468 | },
469 |
470 | drawAngle : function(g, r, theta) {
471 | g.line(this.x, this.y, r * Math.cos(theta) + this.x, r * Math.sin(theta) + this.y);
472 | },
473 |
474 | drawAngleBall : function(g, r, theta, radius) {
475 | g.ellipse(r * Math.cos(theta) + this.x, r * Math.sin(theta) + this.y, radius, radius);
476 | },
477 |
478 | drawArc : function(g, r, theta0, theta1) {
479 | var range = theta1 - theta0;
480 | var segments = Math.ceil(range / .2);
481 | for (var i = 0; i < segments + 1; i++) {
482 | var theta = theta0 + range * (i / segments);
483 | g.vertex(this.x + r * Math.cos(theta), this.y + r * Math.sin(theta));
484 | }
485 | },
486 |
487 | drawText : function(g, s, xOffset, yOffset) {
488 | g.text(s, this.x + xOffset, this.y + yOffset);
489 | },
490 | //===========================================================
491 | //===========================================================
492 |
493 | setScreenPos : function(camera) {
494 | if (!this.screenPos)
495 | this.screenPos = new Vector();
496 | camera.toScreenPos(this, this.screenPos);
497 | },
498 |
499 | toThreeVector : function() {
500 | return new THREE.Vector3(this.x, this.y, this.z);
501 | },
502 | toSVG : function() {
503 | return Math.round(this.x) + " " + Math.round(this.y);
504 | },
505 |
506 | toB2D : function() {
507 | return new Box2D.b2Vec2(this.x, -this.y);
508 | },
509 |
510 | toCSSDimensions : function() {
511 | return {
512 | width : this.x + "px",
513 | height : this.y + "px",
514 |
515 | }
516 | },
517 |
518 | //===========================================================
519 | //===========================================================
520 |
521 | toString : function(precision) {
522 | if (precision === undefined)
523 | precision = 2;
524 |
525 | return "(" + this.x.toFixed(precision) + ", " + this.y.toFixed(precision) + ", " + this.z.toFixed(precision) + ")";
526 | },
527 |
528 | toSimpleString : function() {
529 | precision = 1;
530 | return "(" + this.x.toFixed(precision) + ", " + this.y.toFixed(precision) + ")";
531 |
532 | },
533 |
534 | invalidToString : function() {
535 |
536 | return "(" + this.x + ", " + this.y + ", " + this.z + ")";
537 | },
538 | });
539 |
540 | // Class functions
541 | Vector.sub = function(a, b) {
542 | return new Vector(a.x - b.x, a.y - b.y, a.z - b.z);
543 | };
544 |
545 | Vector.dot = function(a, b) {
546 | return a.x * b.x + a.y * b.y + a.z * b.z;
547 | };
548 |
549 | Vector.polar = function(r, theta) {
550 | return new Vector(r * Math.cos(theta), r * Math.sin(theta));
551 | };
552 |
553 | Vector.polarOffset = function(v, r, theta) {
554 | return new Vector(v.x + r * Math.cos(theta), v.y + r * Math.sin(theta), v.z);
555 | };
556 |
557 | Vector.angleBetween = function(a, b) {
558 | return Math.acos(Vector.dot(a, b) / (a.magnitude() * b.magnitude()));
559 | };
560 |
561 | Vector.addMultiples = function(u, m, v, n, w, o) {
562 | var p = new Vector();
563 | p.addMultiple(u, m);
564 | p.addMultiple(v, n);
565 | if (w)
566 | p.addMultiple(w, o);
567 |
568 | return p;
569 | };
570 |
571 | Vector.add = function(u, v) {
572 | var p = new Vector();
573 | p.add(u);
574 | p.add(v);
575 | return p;
576 | };
577 |
578 | Vector.average = function(array) {
579 | var avg = new Vector();
580 | $.each(array, function(index, v) {
581 | avg.add(v);
582 | });
583 | avg.div(array.length);
584 | return avg;
585 | };
586 |
587 | Vector.getBezierPoint = function(p0, c0, c1, p1, t0) {
588 | var p = new Vector();
589 | p.setToBezierPoint(p0, c0, c1, p1, t0);
590 | return p;
591 | };
592 |
593 | Vector.calculateRayIntersection = function(p, q, u, v) {
594 | var s = Vector.sub(p, u);
595 | var m = (s.y / v.y - s.x / v.x) / (q.x / v.x - q.y / v.y);
596 |
597 | var n0 = s.x / v.x + m * q.x / v.x;
598 |
599 | // for verification
600 | //var n1 = s.y / v.y + m * q.y / v.y;
601 | return [m, n0];
602 | };
603 |
604 | Vector.calculateSegmentIntersection = function(p, p1, u, u1) {
605 | var s = Vector.sub(p, u);
606 |
607 | var q = Vector.sub(p1, p);
608 | var v = Vector.sub(u1, u);
609 |
610 | var m = (s.y / v.y - s.x / v.x) / (q.x / v.x - q.y / v.y);
611 |
612 | var n0 = s.x / v.x + m * q.x / v.x;
613 |
614 | // for verification
615 | //var n1 = s.y / v.y + m * q.y / v.y;
616 | return [m, n0];
617 | };
618 |
619 | return Vector;
620 |
621 | });
622 |
--------------------------------------------------------------------------------
/js/jamboreeUI.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Kate Compton
3 | */
4 |
5 | define(["common", "processing"], function(common, _processing) {'use strict';
6 |
7 | var JamboreeUI = {
8 |
9 | init : function() {
10 | var ui = this;
11 |
12 | ui.mouse = new Vector();
13 | this.createViews();
14 |
15 | $(document).keydown(function(e) {
16 | console.log("key " + e.which);
17 |
18 | var key = String.fromCharCode(event.which);
19 |
20 | if (e.which >= 48 && e.which <= 57)
21 | app.loadTutorial(parseInt(key));
22 |
23 | switch(key) {
24 | case " ":
25 | app.paused = !app.paused;
26 | break;
27 |
28 | }
29 |
30 | });
31 |
32 | this.mouseDown = 0;
33 | document.body.onmousedown = function() {++app.ui.mouseDown;
34 |
35 | };
36 | document.body.onmouseup = function() {--app.ui.mouseDown;
37 | };
38 |
39 | $(document).mouseleave(function() {
40 | console.log("Leave");
41 | app.ui.mouseDown = 0;
42 |
43 | });
44 |
45 | var container = $("#viewProcessing");
46 |
47 | // Mouse responses
48 | container.mousemove(function(ev) {
49 | var offset = $(this).offset();
50 | //or $(this).offset(); if you really just want the current element's offset
51 | var x = ev.pageX - offset.left;
52 | var y = ev.pageY - offset.top;
53 |
54 | ui.mouse.setTo(x - ui.width / 2, y - ui.height / 2);
55 | if (ui.mouseDown) {
56 | if (app.tutorial.onDrag)
57 | app.tutorial.onDrag(ui.mouse);
58 | } else {
59 | if (app.tutorial.onMouseMove)
60 | app.tutorial.onMouseMove(ui.mouse);
61 | }
62 | });
63 |
64 | container.click(function(ev) {
65 | if (app.tutorial.onClick)
66 | app.tutorial.onClick(ui.mouse);
67 |
68 | });
69 |
70 | $("#forwardButton").click(function() {
71 | app.nextTutorial();
72 | });
73 | $("#backButton").click(function() {
74 | app.previousTutorial();
75 | });
76 | },
77 |
78 | //=====================================
79 | createViews : function() {
80 | var container = $("#viewProcessing");
81 | var canvas = container.get(0);
82 |
83 | // attaching the sketchProc function to the canvas
84 | this.processing = new Processing(canvas, function(g) {
85 | var w = container.width();
86 | var h = container.height();
87 | app.ui.width = w;
88 | app.ui.height = h;
89 |
90 | g.size(w, h);
91 | g.colorMode(g.HSB, 1);
92 | g.ellipseMode(g.CENTER_RADIUS);
93 |
94 | g.draw = function() {
95 | app.update();
96 | app.tutorial.update(app.time);
97 |
98 | g.pushMatrix();
99 | // Center the screen
100 | g.translate(w / 2, h / 2);
101 | app.tutorial.draw(g);
102 |
103 | g.popMatrix();
104 | };
105 | });
106 | },
107 |
108 | update : function(time) {
109 |
110 | },
111 | };
112 |
113 | return JamboreeUI;
114 | });
115 |
--------------------------------------------------------------------------------
/js/libs/dancer.js:
--------------------------------------------------------------------------------
1 | /*
2 | * dancer - v0.4.0 - 2014-02-01
3 | * https://github.com/jsantell/dancer.js
4 | * Copyright (c) 2014 Jordan Santell
5 | * Licensed MIT
6 | */
7 | (function() {
8 |
9 | var Dancer = function () {
10 | this.audioAdapter = Dancer._getAdapter( this );
11 | this.events = {};
12 | this.sections = [];
13 | this.bind( 'update', update );
14 | };
15 |
16 | Dancer.version = '0.3.2';
17 | Dancer.adapters = {};
18 |
19 | Dancer.prototype = {
20 |
21 | load : function ( source ) {
22 | var path;
23 |
24 | // Loading an Audio element
25 | if ( source instanceof HTMLElement ) {
26 | this.source = source;
27 | if ( Dancer.isSupported() === 'flash' ) {
28 | this.source = { src: Dancer._getMP3SrcFromAudio( source ) };
29 | }
30 |
31 | // Loading an object with src, [codecs]
32 | } else {
33 | this.source = window.Audio ? new Audio() : {};
34 | this.source.src = Dancer._makeSupportedPath( source.src, source.codecs );
35 | }
36 |
37 | this.audio = this.audioAdapter.load( this.source );
38 | return this;
39 | },
40 |
41 | /* Controls */
42 |
43 | play : function () {
44 | this.audioAdapter.play();
45 | return this;
46 | },
47 |
48 | pause : function () {
49 | this.audioAdapter.pause();
50 | return this;
51 | },
52 |
53 | setVolume : function ( volume ) {
54 | this.audioAdapter.setVolume( volume );
55 | return this;
56 | },
57 |
58 |
59 | /* Actions */
60 |
61 | createKick : function ( options ) {
62 | return new Dancer.Kick( this, options );
63 | },
64 |
65 | bind : function ( name, callback ) {
66 | if ( !this.events[ name ] ) {
67 | this.events[ name ] = [];
68 | }
69 | this.events[ name ].push( callback );
70 | return this;
71 | },
72 |
73 | unbind : function ( name ) {
74 | if ( this.events[ name ] ) {
75 | delete this.events[ name ];
76 | }
77 | return this;
78 | },
79 |
80 | trigger : function ( name ) {
81 | var _this = this;
82 | if ( this.events[ name ] ) {
83 | this.events[ name ].forEach(function( callback ) {
84 | callback.call( _this );
85 | });
86 | }
87 | return this;
88 | },
89 |
90 |
91 | /* Getters */
92 |
93 | getVolume : function () {
94 | return this.audioAdapter.getVolume();
95 | },
96 |
97 | getProgress : function () {
98 | return this.audioAdapter.getProgress();
99 | },
100 |
101 | getTime : function () {
102 | return this.audioAdapter.getTime();
103 | },
104 |
105 | // Returns the magnitude of a frequency or average over a range of frequencies
106 | getFrequency : function ( freq, endFreq ) {
107 | var sum = 0;
108 | if ( endFreq !== undefined ) {
109 | for ( var i = freq; i <= endFreq; i++ ) {
110 | sum += this.getSpectrum()[ i ];
111 | }
112 | return sum / ( endFreq - freq + 1 );
113 | } else {
114 | return this.getSpectrum()[ freq ];
115 | }
116 | },
117 |
118 | getWaveform : function () {
119 | return this.audioAdapter.getWaveform();
120 | },
121 |
122 | getSpectrum : function () {
123 | return this.audioAdapter.getSpectrum();
124 | },
125 |
126 | isLoaded : function () {
127 | return this.audioAdapter.isLoaded;
128 | },
129 |
130 | isPlaying : function () {
131 | return this.audioAdapter.isPlaying;
132 | },
133 |
134 |
135 | /* Sections */
136 |
137 | after : function ( time, callback ) {
138 | var _this = this;
139 | this.sections.push({
140 | condition : function () {
141 | return _this.getTime() > time;
142 | },
143 | callback : callback
144 | });
145 | return this;
146 | },
147 |
148 | before : function ( time, callback ) {
149 | var _this = this;
150 | this.sections.push({
151 | condition : function () {
152 | return _this.getTime() < time;
153 | },
154 | callback : callback
155 | });
156 | return this;
157 | },
158 |
159 | between : function ( startTime, endTime, callback ) {
160 | var _this = this;
161 | this.sections.push({
162 | condition : function () {
163 | return _this.getTime() > startTime && _this.getTime() < endTime;
164 | },
165 | callback : callback
166 | });
167 | return this;
168 | },
169 |
170 | onceAt : function ( time, callback ) {
171 | var
172 | _this = this,
173 | thisSection = null;
174 | this.sections.push({
175 | condition : function () {
176 | return _this.getTime() > time && !this.called;
177 | },
178 | callback : function () {
179 | callback.call( this );
180 | thisSection.called = true;
181 | },
182 | called : false
183 | });
184 | // Baking the section in the closure due to callback's this being the dancer instance
185 | thisSection = this.sections[ this.sections.length - 1 ];
186 | return this;
187 | }
188 | };
189 |
190 | function update () {
191 | for ( var i in this.sections ) {
192 | if ( this.sections[ i ].condition() )
193 | this.sections[ i ].callback.call( this );
194 | }
195 | }
196 |
197 | window.Dancer = Dancer;
198 | })();
199 |
200 | (function ( Dancer ) {
201 |
202 | var CODECS = {
203 | 'mp3' : 'audio/mpeg;',
204 | 'ogg' : 'audio/ogg; codecs="vorbis"',
205 | 'wav' : 'audio/wav; codecs="1"',
206 | 'aac' : 'audio/mp4; codecs="mp4a.40.2"'
207 | },
208 | audioEl = document.createElement( 'audio' );
209 |
210 | Dancer.options = {};
211 |
212 | Dancer.setOptions = function ( o ) {
213 | for ( var option in o ) {
214 | if ( o.hasOwnProperty( option ) ) {
215 | Dancer.options[ option ] = o[ option ];
216 | }
217 | }
218 | };
219 |
220 | Dancer.isSupported = function () {
221 | if ( !window.Float32Array || !window.Uint32Array ) {
222 | return null;
223 | } else if ( !isUnsupportedSafari() && ( window.AudioContext || window.webkitAudioContext )) {
224 | return 'webaudio';
225 | } else if ( audioEl && audioEl.mozSetup ) {
226 | return 'audiodata';
227 | } else if ( FlashDetect.versionAtLeast( 9 ) ) {
228 | return 'flash';
229 | } else {
230 | return '';
231 | }
232 | };
233 |
234 | Dancer.canPlay = function ( type ) {
235 | var canPlay = audioEl.canPlayType;
236 | return !!(
237 | Dancer.isSupported() === 'flash' ?
238 | type.toLowerCase() === 'mp3' :
239 | audioEl.canPlayType &&
240 | audioEl.canPlayType( CODECS[ type.toLowerCase() ] ).replace( /no/, ''));
241 | };
242 |
243 | Dancer.addPlugin = function ( name, fn ) {
244 | if ( Dancer.prototype[ name ] === undefined ) {
245 | Dancer.prototype[ name ] = fn;
246 | }
247 | };
248 |
249 | Dancer._makeSupportedPath = function ( source, codecs ) {
250 | if ( !codecs ) { return source; }
251 |
252 | for ( var i = 0; i < codecs.length; i++ ) {
253 | if ( Dancer.canPlay( codecs[ i ] ) ) {
254 | return source + '.' + codecs[ i ];
255 | }
256 | }
257 | return source;
258 | };
259 |
260 | Dancer._getAdapter = function ( instance ) {
261 | switch ( Dancer.isSupported() ) {
262 | case 'webaudio':
263 | return new Dancer.adapters.webaudio( instance );
264 | case 'audiodata':
265 | return new Dancer.adapters.moz( instance );
266 | case 'flash':
267 | return new Dancer.adapters.flash( instance );
268 | default:
269 | return null;
270 | }
271 | };
272 |
273 | Dancer._getMP3SrcFromAudio = function ( audioEl ) {
274 | var sources = audioEl.children;
275 | if ( audioEl.src ) { return audioEl.src; }
276 | for ( var i = sources.length; i--; ) {
277 | if (( sources[ i ].type || '' ).match( /audio\/mpeg/ )) return sources[ i ].src;
278 | }
279 | return null;
280 | };
281 |
282 | // Browser detection is lame, but Safari 6 has Web Audio API,
283 | // but does not support processing audio from a Media Element Source
284 | // https://gist.github.com/3265344
285 | function isUnsupportedSafari () {
286 | var
287 | isApple = !!( navigator.vendor || '' ).match( /Apple/ ),
288 | version = navigator.userAgent.match( /Version\/([^ ]*)/ );
289 | version = version ? parseFloat( version[ 1 ] ) : 0;
290 | return isApple && version <= 6;
291 | }
292 |
293 | })( window.Dancer );
294 |
295 | (function ( undefined ) {
296 | var Kick = function ( dancer, o ) {
297 | o = o || {};
298 | this.dancer = dancer;
299 | this.frequency = o.frequency !== undefined ? o.frequency : [ 0, 10 ];
300 | this.threshold = o.threshold !== undefined ? o.threshold : 0.3;
301 | this.decay = o.decay !== undefined ? o.decay : 0.02;
302 | this.onKick = o.onKick;
303 | this.offKick = o.offKick;
304 | this.isOn = false;
305 | this.currentThreshold = this.threshold;
306 |
307 | var _this = this;
308 | this.dancer.bind( 'update', function () {
309 | _this.onUpdate();
310 | });
311 | };
312 |
313 | Kick.prototype = {
314 | on : function () {
315 | this.isOn = true;
316 | return this;
317 | },
318 | off : function () {
319 | this.isOn = false;
320 | return this;
321 | },
322 |
323 | set : function ( o ) {
324 | o = o || {};
325 | this.frequency = o.frequency !== undefined ? o.frequency : this.frequency;
326 | this.threshold = o.threshold !== undefined ? o.threshold : this.threshold;
327 | this.decay = o.decay !== undefined ? o.decay : this.decay;
328 | this.onKick = o.onKick || this.onKick;
329 | this.offKick = o.offKick || this.offKick;
330 | },
331 |
332 | onUpdate : function () {
333 | if ( !this.isOn ) { return; }
334 | var magnitude = this.maxAmplitude( this.frequency );
335 | if ( magnitude >= this.currentThreshold &&
336 | magnitude >= this.threshold ) {
337 | this.currentThreshold = magnitude;
338 | this.onKick && this.onKick.call( this.dancer, magnitude );
339 | } else {
340 | this.offKick && this.offKick.call( this.dancer, magnitude );
341 | this.currentThreshold -= this.decay;
342 | }
343 | },
344 | maxAmplitude : function ( frequency ) {
345 | var
346 | max = 0,
347 | fft = this.dancer.getSpectrum();
348 |
349 | // Sloppy array check
350 | if ( !frequency.length ) {
351 | return frequency < fft.length ?
352 | fft[ ~~frequency ] :
353 | null;
354 | }
355 |
356 | for ( var i = frequency[ 0 ], l = frequency[ 1 ]; i <= l; i++ ) {
357 | if ( fft[ i ] > max ) { max = fft[ i ]; }
358 | }
359 | return max;
360 | }
361 | };
362 |
363 | window.Dancer.Kick = Kick;
364 | })();
365 |
366 | (function() {
367 | var
368 | SAMPLE_SIZE = 2048,
369 | SAMPLE_RATE = 44100;
370 |
371 | var adapter = function ( dancer ) {
372 | this.dancer = dancer;
373 | this.audio = new Audio();
374 | this.context = window.AudioContext ?
375 | new window.AudioContext() :
376 | new window.webkitAudioContext();
377 | };
378 |
379 | adapter.prototype = {
380 |
381 | load : function ( _source ) {
382 | var _this = this;
383 | this.audio = _source;
384 |
385 | this.isLoaded = false;
386 | this.progress = 0;
387 |
388 | if (!this.context.createScriptProcessor) {
389 | this.context.createScriptProcessor = this.context.createJavascriptNode;
390 | }
391 | this.proc = this.context.createScriptProcessor( SAMPLE_SIZE / 2, 1, 1 );
392 |
393 | this.proc.onaudioprocess = function ( e ) {
394 | _this.update.call( _this, e );
395 | };
396 | if (!this.context.createGain) {
397 | this.context.createGain = this.context.createGainNode;
398 | }
399 |
400 | this.gain = this.context.createGain();
401 |
402 | this.fft = new FFT( SAMPLE_SIZE / 2, SAMPLE_RATE );
403 | this.signal = new Float32Array( SAMPLE_SIZE / 2 );
404 |
405 | if ( this.audio.readyState < 3 ) {
406 | this.audio.addEventListener( 'canplay', function () {
407 | connectContext.call( _this );
408 | });
409 | } else {
410 | connectContext.call( _this );
411 | }
412 |
413 | this.audio.addEventListener( 'progress', function ( e ) {
414 | if ( e.currentTarget.duration ) {
415 | _this.progress = e.currentTarget.seekable.end( 0 ) / e.currentTarget.duration;
416 | }
417 | });
418 |
419 | return this.audio;
420 | },
421 |
422 | play : function () {
423 | this.audio.play();
424 | this.isPlaying = true;
425 | },
426 |
427 | pause : function () {
428 | this.audio.pause();
429 | this.isPlaying = false;
430 | },
431 |
432 | setVolume : function ( volume ) {
433 | this.gain.gain.value = volume;
434 | },
435 |
436 | getVolume : function () {
437 | return this.gain.gain.value;
438 | },
439 |
440 | getProgress : function() {
441 | return this.progress;
442 | },
443 |
444 | getWaveform : function () {
445 | return this.signal;
446 | },
447 |
448 | getSpectrum : function () {
449 | return this.fft.spectrum;
450 | },
451 |
452 | getTime : function () {
453 | return this.audio.currentTime;
454 | },
455 |
456 | update : function ( e ) {
457 | if ( !this.isPlaying || !this.isLoaded ) return;
458 |
459 | var
460 | buffers = [],
461 | channels = e.inputBuffer.numberOfChannels,
462 | resolution = SAMPLE_SIZE / channels,
463 | sum = function ( prev, curr ) {
464 | return prev[ i ] + curr[ i ];
465 | }, i;
466 |
467 | for ( i = channels; i--; ) {
468 | buffers.push( e.inputBuffer.getChannelData( i ) );
469 | }
470 |
471 | for ( i = 0; i < resolution; i++ ) {
472 | this.signal[ i ] = channels > 1 ?
473 | buffers.reduce( sum ) / channels :
474 | buffers[ 0 ][ i ];
475 | }
476 |
477 | this.fft.forward( this.signal );
478 | this.dancer.trigger( 'update' );
479 | }
480 | };
481 |
482 | function connectContext () {
483 | this.source = this.context.createMediaElementSource( this.audio );
484 | this.source.connect( this.proc );
485 | this.source.connect( this.gain );
486 | this.gain.connect( this.context.destination );
487 | this.proc.connect( this.context.destination );
488 |
489 | this.isLoaded = true;
490 | this.progress = 1;
491 | this.dancer.trigger( 'loaded' );
492 | }
493 |
494 | Dancer.adapters.webaudio = adapter;
495 |
496 | })();
497 |
498 | (function() {
499 |
500 | var adapter = function ( dancer ) {
501 | this.dancer = dancer;
502 | this.audio = new Audio();
503 | };
504 |
505 | adapter.prototype = {
506 |
507 | load : function ( _source ) {
508 | var _this = this;
509 | this.audio = _source;
510 |
511 | this.isLoaded = false;
512 | this.progress = 0;
513 |
514 | if ( this.audio.readyState < 3 ) {
515 | this.audio.addEventListener( 'loadedmetadata', function () {
516 | getMetadata.call( _this );
517 | }, false);
518 | } else {
519 | getMetadata.call( _this );
520 | }
521 |
522 | this.audio.addEventListener( 'MozAudioAvailable', function ( e ) {
523 | _this.update( e );
524 | }, false);
525 |
526 | this.audio.addEventListener( 'progress', function ( e ) {
527 | if ( e.currentTarget.duration ) {
528 | _this.progress = e.currentTarget.seekable.end( 0 ) / e.currentTarget.duration;
529 | }
530 | }, false);
531 |
532 | return this.audio;
533 | },
534 |
535 | play : function () {
536 | this.audio.play();
537 | this.isPlaying = true;
538 | },
539 |
540 | pause : function () {
541 | this.audio.pause();
542 | this.isPlaying = false;
543 | },
544 |
545 | setVolume : function ( volume ) {
546 | this.audio.volume = volume;
547 | },
548 |
549 | getVolume : function () {
550 | return this.audio.volume;
551 | },
552 |
553 | getProgress : function () {
554 | return this.progress;
555 | },
556 |
557 | getWaveform : function () {
558 | return this.signal;
559 | },
560 |
561 | getSpectrum : function () {
562 | return this.fft.spectrum;
563 | },
564 |
565 | getTime : function () {
566 | return this.audio.currentTime;
567 | },
568 |
569 | update : function ( e ) {
570 | if ( !this.isPlaying || !this.isLoaded ) return;
571 |
572 | for ( var i = 0, j = this.fbLength / 2; i < j; i++ ) {
573 | this.signal[ i ] = ( e.frameBuffer[ 2 * i ] + e.frameBuffer[ 2 * i + 1 ] ) / 2;
574 | }
575 |
576 | this.fft.forward( this.signal );
577 | this.dancer.trigger( 'update' );
578 | }
579 | };
580 |
581 | function getMetadata () {
582 | this.fbLength = this.audio.mozFrameBufferLength;
583 | this.channels = this.audio.mozChannels;
584 | this.rate = this.audio.mozSampleRate;
585 | this.fft = new FFT( this.fbLength / this.channels, this.rate );
586 | this.signal = new Float32Array( this.fbLength / this.channels );
587 | this.isLoaded = true;
588 | this.progress = 1;
589 | this.dancer.trigger( 'loaded' );
590 | }
591 |
592 | Dancer.adapters.moz = adapter;
593 |
594 | })();
595 |
596 | (function() {
597 | var
598 | SAMPLE_SIZE = 1024,
599 | SAMPLE_RATE = 44100,
600 | smLoaded = false,
601 | smLoading = false,
602 | CONVERSION_COEFFICIENT = 0.93;
603 |
604 | var adapter = function ( dancer ) {
605 | this.dancer = dancer;
606 | this.wave_L = [];
607 | this.wave_R = [];
608 | this.spectrum = [];
609 | window.SM2_DEFER = true;
610 | };
611 |
612 | adapter.prototype = {
613 | // `source` can be either an Audio element, if supported, or an object
614 | // either way, the path is stored in the `src` property
615 | load : function ( source ) {
616 | var _this = this;
617 | this.path = source ? source.src : this.path;
618 |
619 | this.isLoaded = false;
620 | this.progress = 0;
621 |
622 | !window.soundManager && !smLoading && loadSM.call( this );
623 |
624 | if ( window.soundManager ) {
625 | this.audio = soundManager.createSound({
626 | id : 'dancer' + Math.random() + '',
627 | url : this.path,
628 | stream : true,
629 | autoPlay : false,
630 | autoLoad : true,
631 | whileplaying : function () {
632 | _this.update();
633 | },
634 | whileloading : function () {
635 | _this.progress = this.bytesLoaded / this.bytesTotal;
636 | },
637 | onload : function () {
638 | _this.fft = new FFT( SAMPLE_SIZE, SAMPLE_RATE );
639 | _this.signal = new Float32Array( SAMPLE_SIZE );
640 | _this.waveform = new Float32Array( SAMPLE_SIZE );
641 | _this.isLoaded = true;
642 | _this.progress = 1;
643 | _this.dancer.trigger( 'loaded' );
644 | }
645 | });
646 | this.dancer.audio = this.audio;
647 | }
648 |
649 | // Returns audio if SM already loaded -- otherwise,
650 | // sets dancer instance's audio property after load
651 | return this.audio;
652 | },
653 |
654 | play : function () {
655 | this.audio.play();
656 | this.isPlaying = true;
657 | },
658 |
659 | pause : function () {
660 | this.audio.pause();
661 | this.isPlaying = false;
662 | },
663 |
664 | setVolume : function ( volume ) {
665 | this.audio.setVolume( volume * 100 );
666 | },
667 |
668 | getVolume : function () {
669 | return this.audio.volume / 100;
670 | },
671 |
672 | getProgress : function () {
673 | return this.progress;
674 | },
675 |
676 | getWaveform : function () {
677 | return this.waveform;
678 | },
679 |
680 | getSpectrum : function () {
681 | return this.fft.spectrum;
682 | },
683 |
684 | getTime : function () {
685 | return this.audio.position / 1000;
686 | },
687 |
688 | update : function () {
689 | if ( !this.isPlaying && !this.isLoaded ) return;
690 | this.wave_L = this.audio.waveformData.left;
691 | this.wave_R = this.audio.waveformData.right;
692 | var avg;
693 | for ( var i = 0, j = this.wave_L.length; i < j; i++ ) {
694 | avg = parseFloat(this.wave_L[ i ]) + parseFloat(this.wave_R[ i ]);
695 | this.waveform[ 2 * i ] = avg / 2;
696 | this.waveform[ i * 2 + 1 ] = avg / 2;
697 | this.signal[ 2 * i ] = avg * CONVERSION_COEFFICIENT;
698 | this.signal[ i * 2 + 1 ] = avg * CONVERSION_COEFFICIENT;
699 | }
700 |
701 | this.fft.forward( this.signal );
702 | this.dancer.trigger( 'update' );
703 | }
704 | };
705 |
706 | function loadSM () {
707 | var adapter = this;
708 | smLoading = true;
709 | loadScript( Dancer.options.flashJS, function () {
710 | soundManager = new SoundManager();
711 | soundManager.flashVersion = 9;
712 | soundManager.flash9Options.useWaveformData = true;
713 | soundManager.useWaveformData = true;
714 | soundManager.useHighPerformance = true;
715 | soundManager.useFastPolling = true;
716 | soundManager.multiShot = false;
717 | soundManager.debugMode = false;
718 | soundManager.debugFlash = false;
719 | soundManager.url = Dancer.options.flashSWF;
720 | soundManager.onready(function () {
721 | smLoaded = true;
722 | adapter.load();
723 | });
724 | soundManager.ontimeout(function(){
725 | console.error( 'Error loading SoundManager2.swf' );
726 | });
727 | soundManager.beginDelayedInit();
728 | });
729 | }
730 |
731 | function loadScript ( url, callback ) {
732 | var
733 | script = document.createElement( 'script' ),
734 | appender = document.getElementsByTagName( 'script' )[0];
735 | script.type = 'text/javascript';
736 | script.src = url;
737 | script.onload = callback;
738 | appender.parentNode.insertBefore( script, appender );
739 | }
740 |
741 | Dancer.adapters.flash = adapter;
742 |
743 | })();
744 |
745 | /*
746 | * DSP.js - a comprehensive digital signal processing library for javascript
747 | *
748 | * Created by Corban Brook on 2010-01-01.
749 | * Copyright 2010 Corban Brook. All rights reserved.
750 | *
751 | */
752 |
753 | // Fourier Transform Module used by DFT, FFT, RFFT
754 | function FourierTransform(bufferSize, sampleRate) {
755 | this.bufferSize = bufferSize;
756 | this.sampleRate = sampleRate;
757 | this.bandwidth = 2 / bufferSize * sampleRate / 2;
758 |
759 | this.spectrum = new Float32Array(bufferSize/2);
760 | this.real = new Float32Array(bufferSize);
761 | this.imag = new Float32Array(bufferSize);
762 |
763 | this.peakBand = 0;
764 | this.peak = 0;
765 |
766 | /**
767 | * Calculates the *middle* frequency of an FFT band.
768 | *
769 | * @param {Number} index The index of the FFT band.
770 | *
771 | * @returns The middle frequency in Hz.
772 | */
773 | this.getBandFrequency = function(index) {
774 | return this.bandwidth * index + this.bandwidth / 2;
775 | };
776 |
777 | this.calculateSpectrum = function() {
778 | var spectrum = this.spectrum,
779 | real = this.real,
780 | imag = this.imag,
781 | bSi = 2 / this.bufferSize,
782 | sqrt = Math.sqrt,
783 | rval,
784 | ival,
785 | mag;
786 |
787 | for (var i = 0, N = bufferSize/2; i < N; i++) {
788 | rval = real[i];
789 | ival = imag[i];
790 | mag = bSi * sqrt(rval * rval + ival * ival);
791 |
792 | if (mag > this.peak) {
793 | this.peakBand = i;
794 | this.peak = mag;
795 | }
796 |
797 | spectrum[i] = mag;
798 | }
799 | };
800 | }
801 |
802 | /**
803 | * FFT is a class for calculating the Discrete Fourier Transform of a signal
804 | * with the Fast Fourier Transform algorithm.
805 | *
806 | * @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
807 | * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
808 | *
809 | * @constructor
810 | */
811 | function FFT(bufferSize, sampleRate) {
812 | FourierTransform.call(this, bufferSize, sampleRate);
813 |
814 | this.reverseTable = new Uint32Array(bufferSize);
815 |
816 | var limit = 1;
817 | var bit = bufferSize >> 1;
818 |
819 | var i;
820 |
821 | while (limit < bufferSize) {
822 | for (i = 0; i < limit; i++) {
823 | this.reverseTable[i + limit] = this.reverseTable[i] + bit;
824 | }
825 |
826 | limit = limit << 1;
827 | bit = bit >> 1;
828 | }
829 |
830 | this.sinTable = new Float32Array(bufferSize);
831 | this.cosTable = new Float32Array(bufferSize);
832 |
833 | for (i = 0; i < bufferSize; i++) {
834 | this.sinTable[i] = Math.sin(-Math.PI/i);
835 | this.cosTable[i] = Math.cos(-Math.PI/i);
836 | }
837 | }
838 |
839 | /**
840 | * Performs a forward transform on the sample buffer.
841 | * Converts a time domain signal to frequency domain spectra.
842 | *
843 | * @param {Array} buffer The sample buffer. Buffer Length must be power of 2
844 | *
845 | * @returns The frequency spectrum array
846 | */
847 | FFT.prototype.forward = function(buffer) {
848 | // Locally scope variables for speed up
849 | var bufferSize = this.bufferSize,
850 | cosTable = this.cosTable,
851 | sinTable = this.sinTable,
852 | reverseTable = this.reverseTable,
853 | real = this.real,
854 | imag = this.imag,
855 | spectrum = this.spectrum;
856 |
857 | var k = Math.floor(Math.log(bufferSize) / Math.LN2);
858 |
859 | if (Math.pow(2, k) !== bufferSize) { throw "Invalid buffer size, must be a power of 2."; }
860 | if (bufferSize !== buffer.length) { throw "Supplied buffer is not the same size as defined FFT. FFT Size: " + bufferSize + " Buffer Size: " + buffer.length; }
861 |
862 | var halfSize = 1,
863 | phaseShiftStepReal,
864 | phaseShiftStepImag,
865 | currentPhaseShiftReal,
866 | currentPhaseShiftImag,
867 | off,
868 | tr,
869 | ti,
870 | tmpReal,
871 | i;
872 |
873 | for (i = 0; i < bufferSize; i++) {
874 | real[i] = buffer[reverseTable[i]];
875 | imag[i] = 0;
876 | }
877 |
878 | while (halfSize < bufferSize) {
879 | //phaseShiftStepReal = Math.cos(-Math.PI/halfSize);
880 | //phaseShiftStepImag = Math.sin(-Math.PI/halfSize);
881 | phaseShiftStepReal = cosTable[halfSize];
882 | phaseShiftStepImag = sinTable[halfSize];
883 |
884 | currentPhaseShiftReal = 1;
885 | currentPhaseShiftImag = 0;
886 |
887 | for (var fftStep = 0; fftStep < halfSize; fftStep++) {
888 | i = fftStep;
889 |
890 | while (i < bufferSize) {
891 | off = i + halfSize;
892 | tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]);
893 | ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]);
894 |
895 | real[off] = real[i] - tr;
896 | imag[off] = imag[i] - ti;
897 | real[i] += tr;
898 | imag[i] += ti;
899 |
900 | i += halfSize << 1;
901 | }
902 |
903 | tmpReal = currentPhaseShiftReal;
904 | currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
905 | currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
906 | }
907 |
908 | halfSize = halfSize << 1;
909 | }
910 |
911 | return this.calculateSpectrum();
912 | };
913 |
914 | /*
915 | Copyright (c) Copyright (c) 2007, Carl S. Yestrau All rights reserved.
916 | Code licensed under the BSD License: http://www.featureblend.com/license.txt
917 | Version: 1.0.4
918 | */
919 | var FlashDetect = new function(){
920 | var self = this;
921 | self.installed = false;
922 | self.raw = "";
923 | self.major = -1;
924 | self.minor = -1;
925 | self.revision = -1;
926 | self.revisionStr = "";
927 | var activeXDetectRules = [
928 | {
929 | "name":"ShockwaveFlash.ShockwaveFlash.7",
930 | "version":function(obj){
931 | return getActiveXVersion(obj);
932 | }
933 | },
934 | {
935 | "name":"ShockwaveFlash.ShockwaveFlash.6",
936 | "version":function(obj){
937 | var version = "6,0,21";
938 | try{
939 | obj.AllowScriptAccess = "always";
940 | version = getActiveXVersion(obj);
941 | }catch(err){}
942 | return version;
943 | }
944 | },
945 | {
946 | "name":"ShockwaveFlash.ShockwaveFlash",
947 | "version":function(obj){
948 | return getActiveXVersion(obj);
949 | }
950 | }
951 | ];
952 | /**
953 | * Extract the ActiveX version of the plugin.
954 | *
955 | * @param {Object} The flash ActiveX object.
956 | * @type String
957 | */
958 | var getActiveXVersion = function(activeXObj){
959 | var version = -1;
960 | try{
961 | version = activeXObj.GetVariable("$version");
962 | }catch(err){}
963 | return version;
964 | };
965 | /**
966 | * Try and retrieve an ActiveX object having a specified name.
967 | *
968 | * @param {String} name The ActiveX object name lookup.
969 | * @return One of ActiveX object or a simple object having an attribute of activeXError with a value of true.
970 | * @type Object
971 | */
972 | var getActiveXObject = function(name){
973 | var obj = -1;
974 | try{
975 | obj = new ActiveXObject(name);
976 | }catch(err){
977 | obj = {activeXError:true};
978 | }
979 | return obj;
980 | };
981 | /**
982 | * Parse an ActiveX $version string into an object.
983 | *
984 | * @param {String} str The ActiveX Object GetVariable($version) return value.
985 | * @return An object having raw, major, minor, revision and revisionStr attributes.
986 | * @type Object
987 | */
988 | var parseActiveXVersion = function(str){
989 | var versionArray = str.split(",");//replace with regex
990 | return {
991 | "raw":str,
992 | "major":parseInt(versionArray[0].split(" ")[1], 10),
993 | "minor":parseInt(versionArray[1], 10),
994 | "revision":parseInt(versionArray[2], 10),
995 | "revisionStr":versionArray[2]
996 | };
997 | };
998 | /**
999 | * Parse a standard enabledPlugin.description into an object.
1000 | *
1001 | * @param {String} str The enabledPlugin.description value.
1002 | * @return An object having raw, major, minor, revision and revisionStr attributes.
1003 | * @type Object
1004 | */
1005 | var parseStandardVersion = function(str){
1006 | var descParts = str.split(/ +/);
1007 | var majorMinor = descParts[2].split(/\./);
1008 | var revisionStr = descParts[3];
1009 | return {
1010 | "raw":str,
1011 | "major":parseInt(majorMinor[0], 10),
1012 | "minor":parseInt(majorMinor[1], 10),
1013 | "revisionStr":revisionStr,
1014 | "revision":parseRevisionStrToInt(revisionStr)
1015 | };
1016 | };
1017 | /**
1018 | * Parse the plugin revision string into an integer.
1019 | *
1020 | * @param {String} The revision in string format.
1021 | * @type Number
1022 | */
1023 | var parseRevisionStrToInt = function(str){
1024 | return parseInt(str.replace(/[a-zA-Z]/g, ""), 10) || self.revision;
1025 | };
1026 | /**
1027 | * Is the major version greater than or equal to a specified version.
1028 | *
1029 | * @param {Number} version The minimum required major version.
1030 | * @type Boolean
1031 | */
1032 | self.majorAtLeast = function(version){
1033 | return self.major >= version;
1034 | };
1035 | /**
1036 | * Is the minor version greater than or equal to a specified version.
1037 | *
1038 | * @param {Number} version The minimum required minor version.
1039 | * @type Boolean
1040 | */
1041 | self.minorAtLeast = function(version){
1042 | return self.minor >= version;
1043 | };
1044 | /**
1045 | * Is the revision version greater than or equal to a specified version.
1046 | *
1047 | * @param {Number} version The minimum required revision version.
1048 | * @type Boolean
1049 | */
1050 | self.revisionAtLeast = function(version){
1051 | return self.revision >= version;
1052 | };
1053 | /**
1054 | * Is the version greater than or equal to a specified major, minor and revision.
1055 | *
1056 | * @param {Number} major The minimum required major version.
1057 | * @param {Number} (Optional) minor The minimum required minor version.
1058 | * @param {Number} (Optional) revision The minimum required revision version.
1059 | * @type Boolean
1060 | */
1061 | self.versionAtLeast = function(major){
1062 | var properties = [self.major, self.minor, self.revision];
1063 | var len = Math.min(properties.length, arguments.length);
1064 | for(i=0; i=arguments[i]){
1066 | if(i+10){
1081 | var type = 'application/x-shockwave-flash';
1082 | var mimeTypes = navigator.mimeTypes;
1083 | if(mimeTypes && mimeTypes[type] && mimeTypes[type].enabledPlugin && mimeTypes[type].enabledPlugin.description){
1084 | var version = mimeTypes[type].enabledPlugin.description;
1085 | var versionObj = parseStandardVersion(version);
1086 | self.raw = versionObj.raw;
1087 | self.major = versionObj.major;
1088 | self.minor = versionObj.minor;
1089 | self.revisionStr = versionObj.revisionStr;
1090 | self.revision = versionObj.revision;
1091 | self.installed = true;
1092 | }
1093 | }else if(navigator.appVersion.indexOf("Mac")==-1 && window.execScript){
1094 | var version = -1;
1095 | for(var i=0; i", { size: 1 } ).attr("size") && jQuery.attrFn,
84 | oldAttr = jQuery.attr,
85 | valueAttrGet = jQuery.attrHooks.value && jQuery.attrHooks.value.get ||
86 | function() { return null; },
87 | valueAttrSet = jQuery.attrHooks.value && jQuery.attrHooks.value.set ||
88 | function() { return undefined; },
89 | rnoType = /^(?:input|button)$/i,
90 | rnoAttrNodeType = /^[238]$/,
91 | rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
92 | ruseDefault = /^(?:checked|selected)$/i;
93 |
94 | // jQuery.attrFn
95 | migrateWarnProp( jQuery, "attrFn", attrFn || {}, "jQuery.attrFn is deprecated" );
96 |
97 | jQuery.attr = function( elem, name, value, pass ) {
98 | var lowerName = name.toLowerCase(),
99 | nType = elem && elem.nodeType;
100 |
101 | if ( pass ) {
102 | // Since pass is used internally, we only warn for new jQuery
103 | // versions where there isn't a pass arg in the formal params
104 | if ( oldAttr.length < 4 ) {
105 | migrateWarn("jQuery.fn.attr( props, pass ) is deprecated");
106 | }
107 | if ( elem && !rnoAttrNodeType.test( nType ) &&
108 | (attrFn ? name in attrFn : jQuery.isFunction(jQuery.fn[name])) ) {
109 | return jQuery( elem )[ name ]( value );
110 | }
111 | }
112 |
113 | // Warn if user tries to set `type`, since it breaks on IE 6/7/8; by checking
114 | // for disconnected elements we don't warn on $( "