├── 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 |
41 |
Flow
42 |
43 |
44 | 45 |
46 | by Wvnl 47 |
48 | github 49 |
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