├── README.md
├── STYLE.css
├── index.html
├── js
├── INTERACTION.js
├── MAIN.js
├── METRICS.js
├── PAINT.js
├── UTILS.js
└── lib
│ ├── canvas-noise.js
│ ├── colorflex.js
│ ├── perlin-simplex.js
│ ├── stats.min.js
│ └── tombola.js
└── thumb.jpg
/README.md:
--------------------------------------------------------------------------------
1 | # DEFAULT
2 |
3 | Web version of my paint flow texture algorithm.
4 |
5 | http://whitevinyldesign.com/experiments/flow/
6 |
--------------------------------------------------------------------------------
/STYLE.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /* CSS Document */
3 |
4 |
5 | html {
6 | overflow: scroll;
7 | overflow-x: hidden;
8 | }
9 | ::-webkit-scrollbar {
10 | width: 0; /* remove scrollbar space */
11 | background: transparent; /* optional: just make scrollbar invisible */
12 | }
13 |
14 | body {
15 | margin: 0;
16 | padding: 0;
17 | background-color: rgb(54,49,30);
18 | color: #fff;
19 | width: 100%;
20 |
21 | -webkit-font-smoothing: antialiased;
22 | -moz-font-smoothing: antialiased;
23 | -o-font-smoothing: antialiased;
24 | }
25 |
26 | canvas {
27 | height: 100%;
28 | width: 100%;
29 | display: block;
30 | cursor: default;
31 | overflow: auto;
32 | }
33 |
34 | #noiseLayer {
35 | height: 100%;
36 | width: 100%;
37 | position: absolute;
38 | top: 0;
39 | left: 0;
40 | }
41 |
42 | #overlay {
43 | height: 100%;
44 | width: 100%;
45 | position: absolute;
46 | top: 50px;
47 | left: 0;
48 | overflow: hidden;
49 | display: flex;
50 | align-items: center;
51 | justify-content: center;
52 | flex-direction: column;
53 | opacity: 0;
54 | -webkit-transition: opacity 1s, top 1s;
55 | transition: opacity 1s, top 1s;
56 | pointer-events:none;
57 |
58 | font-size: 90px;
59 | letter-spacing: 0.3em;
60 | }
61 |
62 | #tt {
63 | font-family: 'Lora', serif;
64 | text-transform: uppercase;
65 | text-indent: 0.3em;
66 | }
67 |
68 | #line {
69 | margin-top: 10px;
70 | width: 2.7em;
71 | height: 0.088em;
72 | background-color: #ffffff;
73 | }
74 |
75 | #creator {
76 | position: absolute;
77 | bottom: 20px;
78 | right: 26px;
79 |
80 |
81 | font-family: 'PT Sans', sans-serif;
82 | font-style: italic;
83 | font-size: 12px;
84 | line-height: 1.6em;
85 | letter-spacing: 0.1em;
86 | }
87 |
88 | .divide {
89 | display: inline-block;
90 | height: 1.6em;
91 | width: 1px;
92 | background-color: #ffffff;
93 | margin: 0 3px;
94 | position: relative;
95 | top: 0.4em;
96 | }
97 |
98 | #creator a {
99 | display: inline-block;
100 | padding: 0 3px;
101 | color: inherit;
102 | text-decoration: none;
103 | -webkit-transition: color 0.3s, background-color 0.3s;
104 | transition: color 0.3s, background-color 0.3s;
105 | }
106 |
107 | #creator a:hover {
108 | color: #111111;
109 | background-color: #ffffff;
110 | }
111 |
112 | .unselectable {
113 | -webkit-touch-callout: none;
114 | -webkit-user-select: none;
115 | -khtml-user-select: none;
116 | -moz-user-select: none;
117 | -ms-user-select: none;
118 | user-select: none;
119 | }
120 |
121 | @media (max-width:700px){
122 | #overlay {
123 | font-size: 70px;
124 | }
125 | }
126 | @media (max-width:500px){
127 | #overlay {
128 | font-size: 50px;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | F L O W
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
44 |
45 |
50 |
51 |
52 |
53 |
63 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/js/INTERACTION.js:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | //-------------------------------------------------------------------------------------------
5 | // SETUP
6 | //-------------------------------------------------------------------------------------------
7 |
8 | function setupInteraction() {
9 |
10 | // ADD INTERACTION EVENTS TO THE CANVAS //
11 | canvas.addEventListener("mousedown", mousePress, false);
12 | canvas.addEventListener("mouseup", mouseRelease, false);
13 | canvas.addEventListener("mousemove", mouseMove, false);
14 | }
15 |
16 |
17 | //-------------------------------------------------------------------------------------------
18 | // MOUSE EVENTS
19 | //-------------------------------------------------------------------------------------------
20 |
21 |
22 | // PRESS //
23 | function mousePress() {
24 | mouseIsDown = true;
25 | rolloverCheck();
26 | resetPaint();
27 | }
28 |
29 |
30 | // RELEASE //
31 | function mouseRelease() {
32 | mouseIsDown = false;
33 | }
34 |
35 |
36 | // MOVE //
37 | function mouseMove(event) {
38 | mouseX = event.pageX * ratio;
39 | mouseY = event.pageY * ratio;
40 | rolloverCheck();
41 | }
42 |
43 |
44 | function rolloverCheck() {
45 | var test = hitBox(0, 0, width, height);
46 | }
47 |
--------------------------------------------------------------------------------
/js/MAIN.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | // INIT //
4 | var canvas;
5 | var ctx;
6 | var stats;
7 |
8 | // METRICS //
9 | var width = 0;
10 | var height = 0;
11 | var ratio = 1;
12 | var TAU = 2 * Math.PI;
13 |
14 |
15 | // INTERACTION //
16 | var mouseX = 0;
17 | var mouseY = 0;
18 | var mouseIsDown = false;
19 |
20 | // TEXTURE //
21 | var textureCol = [new RGBA(0,32,185,1),new RGBA(235,98,216,1),new RGBA(10,200,200,1),new RGBA(255,245,235,1),new RGBA(5,5,5,1),new RGBA(255,160,180,1),new RGBA(255,170,170,1),new RGBA(255,140,90,1),new RGBA(245,25,35,1),new RGBA(10,10,70,1),new RGBA(255,80,100,1),new RGBA(70,0,80,1),new RGBA(120,235,200,1),new RGBA(160,150,170,1),new RGBA(220,20,80,1),new RGBA(210,150,120,1)];
22 | var textureCol2 = [new RGBA(0,0,40,1),new RGBA(0,52,65,1),new RGBA(255,230,140,1),new RGBA(255,80,100,1),new RGBA(255,180,210,1)];
23 | var lastPalette = 0;
24 | var paint;
25 | var imgData;
26 |
27 | var addNoise = false;
28 |
29 |
30 | color.lowPass = new RGBA(50,45,25,0);
31 |
32 |
33 | var palettes = [
34 |
35 | [textureCol2[0], textureCol[10], textureCol2[1]], // dark > pink > grey/green
36 | [textureCol2[0], textureCol2[3], textureCol[15]], // flesh > gold
37 | [textureCol2[0], textureCol2[1], textureCol2[2]], // dark green > yellow
38 | [textureCol[4], textureCol2[1], textureCol[10]], // petrel > pink
39 | [textureCol[4], textureCol[9], textureCol[11]], // dark > purple
40 | [textureCol[4], textureCol2[0], textureCol[11]], // extra dark > purple
41 | [textureCol[4], textureCol[11], textureCol[12]], // purple > turquoise
42 | [textureCol2[1], textureCol[14], textureCol[10]], // dark > red
43 | [textureCol[9], textureCol[10], textureCol[12]], // dark purple > flesh > turquoise
44 | [textureCol2[1], textureCol[10], textureCol[12]], // dark > flesh > turquoise
45 | [textureCol[4], textureCol[9], textureCol[14]], // dark purple > magenta
46 | [textureCol[4], textureCol[9], textureCol[12]], // dark > turquoise
47 | [textureCol[4], textureCol[9], textureCol[8]], // dark purple > red
48 | [textureCol2[0], textureCol2[3], textureCol[6]], // pinks
49 | [textureCol[4], textureCol2[0], textureCol2[3]], // very dark > pink
50 | [textureCol[4], textureCol2[0], textureCol[7]], // dark > gold
51 | [textureCol[4], textureCol[9], textureCol[7]], // dark blue > gold
52 | [textureCol2[0], textureCol2[1], textureCol[10]], // dark blue/green > pink
53 | [textureCol[4], textureCol2[1], textureCol[14]], // dark green > magenta
54 | [textureCol2[0], textureCol[13], textureCol[5]], // grey > pink marble
55 | [textureCol[10], textureCol[13], textureCol[5]], // pink > grey > pink flamingo
56 | [textureCol2[1], textureCol[13], textureCol[10]], // grey green > coral
57 | [textureCol[3], textureCol[13], textureCol[10]], // white grey > pink * not sure
58 | [textureCol[3], textureCol[13], textureCol[9]], // white grey > dark purple
59 | [textureCol[4], textureCol[9], textureCol2[1]], // dark green > blue
60 | [textureCol[3], textureCol2[0], textureCol[4]], // white > dark
61 | [textureCol2[0], textureCol2[1], textureCol[7]], // dark green > gold
62 | [textureCol2[0], textureCol[9], textureCol[5]], // navy > bubblegum pink
63 | [textureCol[6], textureCol[13], textureCol[10]], // pale pink > grey coral
64 | [textureCol[4], textureCol2[0], textureCol[15]], // dark > cream
65 | [textureCol[4], textureCol[9], textureCol[13]], // dark blue > pale grey
66 | [textureCol[4], textureCol[9], textureCol[6]], // dark blue > pale pink
67 | [textureCol[4], textureCol[9], textureCol[10]], // dark blue > coral pink
68 | [textureCol2[0], textureCol[0], textureCol[5]], // electric blue > bubblegum
69 | [textureCol[0], textureCol2[0], textureCol[4]] // dark > electric blue
70 | ];
71 |
72 |
73 |
74 |
75 | //-------------------------------------------------------------------------------------------
76 | // INITIALISE
77 | //-------------------------------------------------------------------------------------------
78 |
79 | function init() {
80 |
81 | // SETUP EXPERIMENT //
82 | setupExperiment();
83 |
84 | // SETUP CANVAS //
85 | canvas = document.getElementById('canvas');
86 | ctx = canvas.getContext('2d');
87 |
88 |
89 | // SET CANVAS & DRAWING POSITIONS //
90 | metrics();
91 |
92 | // INTERACTION //
93 | //setupInteraction();
94 |
95 | // STATS //
96 | //initStats();
97 |
98 | // GENERATE NOISE LAYER //
99 | canvasNoise(200, 200, ratio, 0.025, 'noiseLayer');
100 |
101 |
102 | // CSS TRANSITION IN //
103 | var overlay = document.getElementById('overlay');
104 | overlay.style.top = '0';
105 | overlay.style.opacity = '1';
106 |
107 | setTimeout(function() {
108 | // INIT PAINT //
109 | resetPaint();
110 |
111 | // START LOOP //
112 | loop();
113 | }, 1000);
114 | }
115 |
116 | function resetPaint() {
117 |
118 | // choose palette and store memory to prevent repetition //
119 | var ind = lastPalette;
120 | while (ind === lastPalette) {
121 | ind = tombola.range(0, palettes.length - 1);
122 | }
123 | var p = palettes[ind];
124 | lastPalette = ind;
125 | console.log(ind);
126 |
127 | paint = new Paint(ctx, width, height, tombola.rangeFloat(0.6, 2), p[0], p[1], p[2], 0.05, 0.3);
128 | }
129 |
130 |
131 | function initStats() {
132 | stats = new Stats();
133 | stats.showPanel( 0 ); // 0: fps, 1: ms, 2: mb, 3+: custom
134 | document.body.appendChild( stats.dom );
135 | }
136 |
137 |
138 | //-------------------------------------------------------------------------------------------
139 | // MAIN LOOP
140 | //-------------------------------------------------------------------------------------------
141 |
142 |
143 | function loop() {
144 | if (stats) stats.begin();
145 | update();
146 | draw();
147 | if (stats) stats.end();
148 | requestAnimationFrame(loop);
149 | }
150 |
151 |
152 | //-------------------------------------------------------------------------------------------
153 | // UPDATE
154 | //-------------------------------------------------------------------------------------------
155 |
156 | function update() {
157 | if (experiment) {
158 | experiment.update();
159 | }
160 | }
161 |
162 |
163 | //-------------------------------------------------------------------------------------------
164 | // DRAW
165 | //-------------------------------------------------------------------------------------------
166 |
167 | function draw() {
168 | paint.draw(4);
169 | }
170 |
--------------------------------------------------------------------------------
/js/METRICS.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | //-------------------------------------------------------------------------------------------
4 | // METRICS
5 | //-------------------------------------------------------------------------------------------
6 |
7 | function metrics() {
8 |
9 | // GET DISPLAY DIMENSIONS //
10 | ratio = getPixelRatio();
11 | width = window.innerWidth * ratio;
12 | height = window.innerHeight * ratio;
13 |
14 |
15 | // SET CANVAS DIMENSIONS //
16 | canvas.width = width;
17 | canvas.height = height;
18 | }
19 |
20 |
21 | //-------------------------------------------------------------------------------------------
22 | // PIXEL RATIO
23 | //-------------------------------------------------------------------------------------------
24 |
25 | function getPixelRatio() {
26 | var dpr = window.devicePixelRatio || 1;
27 | var bsr = ctx.webkitBackingStorePixelRatio ||
28 | ctx.mozBackingStorePixelRatio ||
29 | ctx.msBackingStorePixelRatio ||
30 | ctx.oBackingStorePixelRatio ||
31 | ctx.backingStorePixelRatio || 1;
32 | return dpr / bsr;
33 | }
34 |
--------------------------------------------------------------------------------
/js/PAINT.js:
--------------------------------------------------------------------------------
1 |
2 | function Paint(ctx,width,height,scale,col1,col2,col3,contrast,banding) {
3 |
4 | this.i = -1;
5 | this.j = 0;
6 | this.completeCols = [];
7 |
8 |
9 | this.ctx = ctx;
10 | this.col1 = col1;
11 | this.col2 = col2;
12 | this.col3 = col3;
13 |
14 | this.noiseLevel = 4 * ratio;
15 | //this.noiseLevel = 255 * ratio;
16 |
17 | this.thickness = 3;
18 |
19 | // generate texture //
20 | this.simplex = new SimplexNoise();
21 | this.rowHeight = 135 * scale;
22 | this.height = Math.ceil(height);
23 | this.width = Math.ceil(width);
24 |
25 |
26 | this.contrast = contrast * 100;
27 | this.cells = this.width; // necessary?
28 | this.streakIndex = 0;
29 | this.rowOffset = 0;
30 |
31 |
32 | // make scale relative to canvas size //
33 | scale *= (Math.max(this.width,this.height)/(255 * ratio));
34 | this.wobbleHeight = tombola.rangeFloat(17,26) * scale;
35 | this.driftHeight = 140 * scale;
36 |
37 | // total offset potential //
38 | this.vertOffset = (this.rowHeight + this.wobbleHeight + this.driftHeight);
39 |
40 | this.banding = (banding || 0.8) * (scale/1);
41 | this.pScale = this.banding/scale; // scale of chance percentage, color shift
42 | this.scale = scale * 400;
43 |
44 | // perlin scales //
45 | this.heightX = this.scale * 1.5;
46 | this.heightY = this.scale * 2;
47 | this.wobbleX = this.scale / 2;
48 | this.wobbleY = this.scale / 1.5;
49 | this.driftY = this.scale * 1.6;
50 | this.colorY = this.scale * 2;
51 |
52 | this.rows = this.height + (this.vertOffset * 2);
53 | this._newRow();
54 | }
55 |
56 |
57 | Paint.prototype.draw = function(speed) {
58 |
59 | // if there are rows to be drawn //
60 | if (this.i < this.rows) {
61 | var ctx = this.ctx;
62 |
63 | // loop through rows * speed //
64 | var l = this.width * speed;
65 | for (var h=0; h= (this.width-2)) {
82 | this.specks();
83 | this.i = this.rows;
84 | setTimeout(function() {
85 | resetPaint();
86 | }, 800);
87 | return;
88 | }
89 |
90 | // strike off complete filled columns //
91 | if (pos >= height) {
92 | if (this.completeCols.indexOf(this.j) === -1) {
93 | this.completeCols.push(this.j);
94 | }
95 | this._proceed();
96 | continue;
97 | }
98 |
99 | // color value & contrast //
100 | var n = this.simplex.noise(this.streakIndex, (this.j + this.rowOffset) / this.colorY);
101 | n += (Math.sign(n) * 0.01 * this.contrast);
102 | n = (n + 1) / 2; // normalise to range 0 - 1;
103 |
104 | // set blended fill color //
105 | var fillCol;
106 | if (n > 0.5) {
107 | n = (n - 0.5) * 2;
108 | fillCol = color.blend2(this.col2, this.col3, n * 100);
109 | } else {
110 | n *= 2;
111 | fillCol = color.blend2(this.col1, this.col2, n * 100);
112 | }
113 |
114 | // add noise to color //
115 | if (addNoise) {
116 | var noiseLvl = tombola.rangeFloat(-this.noiseLevel,this.noiseLevel);
117 | fillCol.R += noiseLvl;
118 | fillCol.G += noiseLvl;
119 | fillCol.B += noiseLvl;
120 | }
121 |
122 |
123 | // draw //
124 | color.fill(ctx, fillCol );
125 | ctx.fillRect(this.j,pos, 1, this.thickness);
126 |
127 | // done //
128 | this._proceed();
129 | }
130 |
131 | }
132 | };
133 |
134 | Paint.prototype._proceed = function() {
135 | this.j ++;
136 | if (this.j >= this.width) {
137 | this._newRow();
138 | }
139 | }
140 |
141 | Paint.prototype._newRow = function() {
142 | this.i ++;
143 | this.j = 0;
144 |
145 | // progress perlin horizontal index for color //
146 | this.rowOffset += tombola.rangeFloat(-10,10);
147 |
148 | // progress perlin vertical index for color //
149 | var sm = 0.6;
150 | this.streakIndex += tombola.rangeFloat(-0.05 * sm,0.05 * sm);
151 | if (tombola.percent(1.2 * this.pScale)) {
152 | this.streakIndex += tombola.rangeFloat(0.2 * sm,0.3 * sm); // larger jump
153 | }
154 | else if (tombola.percent(0.7 * this.pScale)) {
155 | this.streakIndex += tombola.rangeFloat(1 * sm,2 * sm); // larger still
156 | }
157 | };
158 |
159 | Paint.prototype.specks = function() {
160 |
161 | // specks //
162 | if (tombola.percent(40)) {
163 |
164 | // number of clusters //
165 | var clusterNo = tombola.weightedNumber([3,2,1,1]);
166 |
167 | // scale //
168 | var sc = this.scale/(1040/ratio);
169 | var maxSize = 1.1;
170 |
171 | // color //
172 | fillCol = color.blend2(this.col1, this.col2, tombola.range(0, 50));
173 | color.fill(ctx, fillCol);
174 |
175 | // for each cluster of specks //
176 | for (j=0; jx && mx<(x+w) && my>y && my<(y+h));
13 | }
14 |
15 |
16 |
17 | // LOCK A VALUE WITHIN GIVEN RANGE //
18 | function valueInRange(value,floor,ceiling) {
19 | if (value < floor) {
20 | value = floor;
21 | }
22 | if (value> ceiling) {
23 | value = ceiling;
24 | }
25 | return value;
26 | }
27 |
28 |
29 |
30 | // LERP TWEEN / EASE //
31 | function lerp(current,destination,speed) {
32 | return current + (((destination-current)/100) * speed);
33 | }
34 |
35 |
36 |
37 | // IS VAL A NEAR TO VAL B //
38 | function near(a,b,factor) {
39 | return Math.round(a/factor) == Math.round(b/factor);
40 | }
41 |
42 |
43 | function decimaRound(n,places) {
44 | var p = Math.pow(10,places);
45 | return Math.round(n * p) / p;
46 | }
47 |
48 |
49 | function degToRad(deg) {
50 | return deg * (Math.PI/180);
51 | }
52 |
53 | function radToDeg(rad) {
54 | return (rad/TAU) * 180;
55 | }
56 |
57 | function getRadius(a,b) {
58 | return Math.sqrt((a*a)+(b*b));
59 | }
60 |
61 | function angleFromVector(vector) {
62 | return Math.atan2(vector.y,vector.a);
63 | }
64 |
65 | function vectorFromAngle(angle) {
66 | return new Vector(Math.cos(angle),Math.sin(angle));
67 | }
68 |
69 | function removeFromArray(item, array) {
70 | var index = array.indexOf(item);
71 | if (index > -1) {
72 | array.splice(index, 1);
73 | }
74 | }
75 |
76 | function distanceInRange(position1, position2, range) {
77 | return distanceBetweenPoints(position1, position2) < range;
78 | }
79 |
80 | function angleBetweenPoints(position1, position2) {
81 | return Math.atan2(position2.y - position1.y, position2.x - position1.x);
82 | }
83 |
84 | function distanceBetweenPoints(position1, position2) {
85 | var a = position1.x - position2.x;
86 | var b = position1.y - position2.y;
87 | return Math.sqrt( a*a + b*b );
88 | }
89 |
90 | function normaliseAngle(angle) {
91 | while (angle > TAU) {
92 | angle -= TAU;
93 | }
94 | while (angle < 0) {
95 | angle += TAU;
96 | }
97 | return angle;
98 | }
99 |
100 | //-------------------------------------------------------------------------------------------
101 | // OBJECTS
102 | //-------------------------------------------------------------------------------------------
103 |
104 |
105 | function Point( x, y ) {
106 | this.x = x || 0;
107 | this.y = y || 0;
108 | }
109 | Point.prototype.clone = function() {
110 | return new Point(this.x,this.y);
111 | };
112 |
113 | function Point3D( x, y, z ) {
114 | this.x = x || 0;
115 | this.y = y || 0;
116 | this.z = z || 0;
117 | }
118 |
119 | function Vector( x, y ) {
120 | this.x = x || 0;
121 | this.y = y || 0;
122 | }
123 | Vector.prototype.clone = function() {
124 | return new Vector(this.x,this.y);
125 | };
126 | Vector.prototype.magnitude = function() {
127 | return Math.sqrt((this.x*this.x) + (this.y*this.y));
128 | };
129 |
130 | Vector.prototype.normalise = function() {
131 | var m = this.magnitude();
132 | if (m>0) {
133 | this.x /= m;
134 | this.y /= m;
135 | }
136 | };
137 |
138 | function Size( w, h ) {
139 | this.w = w || 0;
140 | this.h = h || 0;
141 | }
142 |
143 |
144 | function Alpha(a) {
145 | this.a = a || 0;
146 | }
147 |
--------------------------------------------------------------------------------
/js/lib/canvas-noise.js:
--------------------------------------------------------------------------------
1 |
2 | function canvasNoise(w, h, pixelRatio, alpha, element) {
3 | var cvs = document.createElement('canvas');
4 | var ct = cvs.getContext('2d');
5 |
6 | var rw = w * pixelRatio;
7 | var rh = h * pixelRatio;
8 | cvs.width = rw;
9 | cvs.height = rh;
10 |
11 | alpha *= pixelRatio;
12 |
13 |
14 | for (var j=0; j ceiling) {
130 | value = ceiling;
131 | }
132 | return value;
133 | };
134 |
135 | Color.prototype.getLuminosity = function(col) {
136 | return ((0.299*col.R + 0.587*col.G + 0.114*col.B));
137 | };
138 |
139 | //-------------------------------------------------------------------------------------------
140 | // RGBA
141 | //-------------------------------------------------------------------------------------------
142 |
143 | function RGBA( r, g, b, a ) {
144 | this.R = r;
145 | this.G = g;
146 | this.B = b;
147 | this.A = a;
148 | }
149 | RGBA.prototype.clone = function() {
150 | return new RGBA(this.R,this.G,this.B,this.A);
151 | };
152 |
153 | //module.exports = Color;
154 |
--------------------------------------------------------------------------------
/js/lib/perlin-simplex.js:
--------------------------------------------------------------------------------
1 | // https://gist.github.com/banksean/304522
2 | //
3 | // Ported from Stefan Gustavson's java implementation
4 | // http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
5 | // Read Stefan's excellent paper for details on how this code works.
6 | //
7 | // Sean McCullough banksean@gmail.com
8 |
9 | /**
10 | * You can pass in a random number generator object if you like.
11 | * It is assumed to have a random() method.
12 | */
13 | function SimplexNoise(r) {
14 | if (r == undefined) r = Math;
15 | this.grad3 = [[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0],
16 | [1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1],
17 | [0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]];
18 | this.p = [];
19 | for (var i=0; i<256; i++) {
20 | this.p[i] = Math.floor(r.random()*256);
21 | }
22 | // To remove the need for index wrapping, double the permutation table length
23 | this.perm = [];
24 | for(var i=0; i<512; i++) {
25 | this.perm[i]=this.p[i & 255];
26 | }
27 |
28 | // A lookup table to traverse the simplex around a given point in 4D.
29 | // Details can be found where this table is used, in the 4D noise method.
30 | this.simplex = [
31 | [0,1,2,3],[0,1,3,2],[0,0,0,0],[0,2,3,1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[1,2,3,0],
32 | [0,2,1,3],[0,0,0,0],[0,3,1,2],[0,3,2,1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[1,3,2,0],
33 | [0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],
34 | [1,2,0,3],[0,0,0,0],[1,3,0,2],[0,0,0,0],[0,0,0,0],[0,0,0,0],[2,3,0,1],[2,3,1,0],
35 | [1,0,2,3],[1,0,3,2],[0,0,0,0],[0,0,0,0],[0,0,0,0],[2,0,3,1],[0,0,0,0],[2,1,3,0],
36 | [0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],
37 | [2,0,1,3],[0,0,0,0],[0,0,0,0],[0,0,0,0],[3,0,1,2],[3,0,2,1],[0,0,0,0],[3,1,2,0],
38 | [2,1,0,3],[0,0,0,0],[0,0,0,0],[0,0,0,0],[3,1,0,2],[0,0,0,0],[3,2,0,1],[3,2,1,0]];
39 | }
40 |
41 | SimplexNoise.prototype.dot = function(g, x, y) {
42 | return g[0]*x + g[1]*y;
43 | };
44 |
45 | SimplexNoise.prototype.noise = function(xin, yin) {
46 | var n0, n1, n2; // Noise contributions from the three corners
47 | // Skew the input space to determine which simplex cell we're in
48 | var F2 = 0.5*(Math.sqrt(3.0)-1.0);
49 | var s = (xin+yin)*F2; // Hairy factor for 2D
50 | var i = Math.floor(xin+s);
51 | var j = Math.floor(yin+s);
52 | var G2 = (3.0-Math.sqrt(3.0))/6.0;
53 | var t = (i+j)*G2;
54 | var X0 = i-t; // Unskew the cell origin back to (x,y) space
55 | var Y0 = j-t;
56 | var x0 = xin-X0; // The x,y distances from the cell origin
57 | var y0 = yin-Y0;
58 | // For the 2D case, the simplex shape is an equilateral triangle.
59 | // Determine which simplex we are in.
60 | var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
61 | if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1)
62 | else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1)
63 | // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
64 | // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
65 | // c = (3-sqrt(3))/6
66 | var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
67 | var y1 = y0 - j1 + G2;
68 | var x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords
69 | var y2 = y0 - 1.0 + 2.0 * G2;
70 | // Work out the hashed gradient indices of the three simplex corners
71 | var ii = i & 255;
72 | var jj = j & 255;
73 | var gi0 = this.perm[ii+this.perm[jj]] % 12;
74 | var gi1 = this.perm[ii+i1+this.perm[jj+j1]] % 12;
75 | var gi2 = this.perm[ii+1+this.perm[jj+1]] % 12;
76 | // Calculate the contribution from the three corners
77 | var t0 = 0.5 - x0*x0-y0*y0;
78 | if(t0<0) n0 = 0.0;
79 | else {
80 | t0 *= t0;
81 | n0 = t0 * t0 * this.dot(this.grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient
82 | }
83 | var t1 = 0.5 - x1*x1-y1*y1;
84 | if(t1<0) n1 = 0.0;
85 | else {
86 | t1 *= t1;
87 | n1 = t1 * t1 * this.dot(this.grad3[gi1], x1, y1);
88 | }
89 | var t2 = 0.5 - x2*x2-y2*y2;
90 | if(t2<0) n2 = 0.0;
91 | else {
92 | t2 *= t2;
93 | n2 = t2 * t2 * this.dot(this.grad3[gi2], x2, y2);
94 | }
95 | // Add contributions from each corner to get the final noise value.
96 | // The result is scaled to return values in the interval [-1,1].
97 | return 70.0 * (n0 + n1 + n2);
98 | };
99 |
100 | // 3D simplex noise
101 | SimplexNoise.prototype.noise3d = function(xin, yin, zin) {
102 | var n0, n1, n2, n3; // Noise contributions from the four corners
103 | // Skew the input space to determine which simplex cell we're in
104 | var F3 = 1.0/3.0;
105 | var s = (xin+yin+zin)*F3; // Very nice and simple skew factor for 3D
106 | var i = Math.floor(xin+s);
107 | var j = Math.floor(yin+s);
108 | var k = Math.floor(zin+s);
109 | var G3 = 1.0/6.0; // Very nice and simple unskew factor, too
110 | var t = (i+j+k)*G3;
111 | var X0 = i-t; // Unskew the cell origin back to (x,y,z) space
112 | var Y0 = j-t;
113 | var Z0 = k-t;
114 | var x0 = xin-X0; // The x,y,z distances from the cell origin
115 | var y0 = yin-Y0;
116 | var z0 = zin-Z0;
117 | // For the 3D case, the simplex shape is a slightly irregular tetrahedron.
118 | // Determine which simplex we are in.
119 | var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
120 | var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
121 | if(x0>=y0) {
122 | if(y0>=z0)
123 | { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order
124 | else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order
125 | else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order
126 | }
127 | else { // x0g+1E3&&(r.update(1E3*a/(c-g),100),g=c,a=0,t)){var d=performance.memory;t.update(d.usedJSHeapSize/
4 | 1048576,d.jsHeapSizeLimit/1048576)}return c},update:function(){k=this.end()},domElement:c,setMode:u}};f.Panel=function(e,f,l){var c=Infinity,k=0,g=Math.round,a=g(window.devicePixelRatio||1),r=80*a,h=48*a,t=3*a,v=2*a,d=3*a,m=15*a,n=74*a,p=30*a,q=document.createElement("canvas");q.width=r;q.height=h;q.style.cssText="width:80px;height:48px";var b=q.getContext("2d");b.font="bold "+9*a+"px Helvetica,Arial,sans-serif";b.textBaseline="top";b.fillStyle=l;b.fillRect(0,0,r,h);b.fillStyle=f;b.fillText(e,t,v);
5 | b.fillRect(d,m,n,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d,m,n,p);return{dom:q,update:function(h,w){c=Math.min(c,h);k=Math.max(k,h);b.fillStyle=l;b.globalAlpha=1;b.fillRect(0,0,r,m);b.fillStyle=f;b.fillText(g(h)+" "+e+" ("+g(c)+"-"+g(k)+")",t,v);b.drawImage(q,d+a,m,n-a,p,d,m,n-a,p);b.fillRect(d+n-a,m,a,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d+n-a,m,a,g((1-h/w)*p))}}};return f});
6 |
--------------------------------------------------------------------------------
/js/lib/tombola.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | //-------------------------------------------------------------------------------------------
4 | // TOMBOLA SETUP
5 | //-------------------------------------------------------------------------------------------
6 |
7 | function Tombola() {
8 | }
9 | var tombola = new Tombola();
10 |
11 |
12 | //-------------------------------------------------------------------------------------------
13 | // RANGE ROLL
14 | //-------------------------------------------------------------------------------------------
15 |
16 | // Returns a random whole number between 'min' and 'max' //
17 |
18 | Tombola.prototype.range = function(min,max) {
19 | return Math.round(min + (Math.random() * (max - min))); // int
20 | };
21 |
22 | // Returns a random float number between 'min' and 'max' //
23 |
24 | Tombola.prototype.rangeFloat = function(min,max) {
25 | return min + (Math.random() * (max - min)); // float
26 | };
27 |
28 | // Returns an array populated with random whole numbers between 'min' and 'max' //
29 |
30 | Tombola.prototype.rangeArray = function(min,max,length) {
31 | var a = [];
32 | for (var i=0; i0) {
313 | index = index || Math.floor(Math.random() * this.contents.length);
314 | var item = this.contents[index];
315 | this.contents.splice(index,1);
316 | return item;
317 | } else {
318 | return null;
319 | }
320 | };
321 |
322 | // Returns an item from the deck, randomly or at a given index, the item stays in the deck //
323 | RandomDeck.prototype.look = function(index) {
324 | if (this.contents.length>0) {
325 | index = index || Math.floor(Math.random() * this.contents.length);
326 | return this.contents[index];
327 | } else {
328 | return null;
329 | }
330 | };
331 |
332 | // Adds an item to the deck, randomly or at a given index //
333 | RandomDeck.prototype.insert = function(item, index) {
334 | index = index || Math.round(Math.random() * this.contents.length);
335 | this.contents.splice(index,0,item);
336 | };
337 |
338 | // Shuffles the deck order //
339 | RandomDeck.prototype.shuffle = function() {
340 | var a = [];
341 | var l = this.contents.length;
342 | for (var i=0; i0) {
389 |
390 | // no given index, do random weighting //
391 | var l = this.contents.length;
392 | if (!(index >=0 && index0) {
426 |
427 | // no given index, do random weighting //
428 | var l = this.contents.length;
429 | if (!(index >=0 && index