├── .gitignore ├── Gruntfile.js ├── README.md ├── bitlib.js ├── bitlib.min.js ├── package.json ├── src ├── bitlib.js ├── bitlib_anim.js ├── bitlib_color.js ├── bitlib_context.js ├── bitlib_image.js ├── bitlib_math.js ├── bitlib_random.js └── chooser.js └── test ├── boyhowdy.jpg ├── testPath.html ├── testPower.html ├── testTangent.html ├── testanim.html ├── testanim.js ├── testbezier.html ├── testbezier.js ├── testcolors.html ├── testcolors.js ├── testgauss.html ├── testgauss.js ├── testimage.html ├── testimage.js ├── testmulticurve.html ├── testmulticurve.js ├── testpath.js ├── testpower.js ├── testrandom.html ├── testrandom.js ├── testshadow.html ├── testshadow.js └── testtangent.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.loadNpmTasks("grunt-contrib-copy"); 3 | grunt.loadNpmTasks("grunt-contrib-watch"); 4 | grunt.loadNpmTasks("grunt-contrib-uglify"); 5 | 6 | grunt.initConfig({ 7 | copy: { 8 | main: { 9 | src: "src/bitlib.js", 10 | dest: "bitlib.js", 11 | options: { 12 | process: function(content, srcPath) { 13 | var math = grunt.file.read("src/bitlib_math.js"), 14 | context = grunt.file.read("src/bitlib_context.js"), 15 | color = grunt.file.read("src/bitlib_color.js"), 16 | random = grunt.file.read("src/bitlib_random.js"), 17 | anim = grunt.file.read("src/bitlib_anim.js"), 18 | image = grunt.file.read("src/bitlib_image.js"), 19 | 20 | content = content.replace("${bitlib_math}", math); 21 | content = content.replace("${bitlib_context}", context); 22 | content = content.replace("${bitlib_color}", color); 23 | content = content.replace("${bitlib_random}", random); 24 | content = content.replace("${bitlib_anim}", anim); 25 | content = content.replace("${bitlib_image}", image); 26 | return content; 27 | } 28 | } 29 | } 30 | }, 31 | 32 | watch: { 33 | main: { 34 | files: "src/*.js", 35 | tasks: ["build"] 36 | } 37 | }, 38 | 39 | uglify: { 40 | main: { 41 | files: { 42 | 'bitlib.min.js': ["bitlib.js"] 43 | } 44 | } 45 | } 46 | 47 | }); 48 | 49 | grunt.registerTask("build", ["copy", "uglify"]); 50 | 51 | 52 | }; 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bitlib 2 | A collection of useful JS utils. Mostly math and graphics related. 3 | 4 | I just made this for myself - a collection of functions that I find useful and make experimental / creative coding easier. Not something you'd necessarily want to include wholesale in production code. Feel free to use it however you want though. This will grow and change over time. 5 | -------------------------------------------------------------------------------- /bitlib.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var bitlib = {}; 3 | 4 | bitlib.math = { 5 | norm: function (value, min, max) { 6 | return (value - min) / (max - min); 7 | }, 8 | 9 | lerp: function (min, max, t) { 10 | return min + (max - min) * t; 11 | }, 12 | 13 | map: function (srcValue, srcMin, srcMax, dstMin, dstMax) { 14 | var norm = this.norm(srcValue, srcMin, srcMax); 15 | return this.lerp(dstMin, dstMax, norm); 16 | }, 17 | 18 | clamp: function (value, min, max) { 19 | return Math.min(Math.max(value, min), max); 20 | }, 21 | 22 | dotProduct: function(x0, y0, x1, y1, x2, y2, x3, y3) { 23 | var dx0 = x1 - x0, 24 | dy0 = y1 - y0, 25 | dx1 = x3 - x2, 26 | dy1 = y3 - y2; 27 | return dx0 * dx1 + dy0 * dy1; 28 | }, 29 | 30 | angleBetween: function(x0, y0, x1, y1, x2, y2, x3, y3) { 31 | var dp = this.dotProduct(x0, y0, x1, y1, x2, y2, x3, y3), 32 | mag0 = this.dist(x0, y0, x1, y1), 33 | mag1 = this.dist(x2, y2, x3, y3); 34 | return Math.acos(dp / mag0 / mag1); 35 | }, 36 | 37 | polarToPoint: function (angle, radius) { 38 | return { 39 | x: Math.cos(angle) * radius, 40 | y: Math.sin(angle) * radius 41 | }; 42 | }, 43 | 44 | pointToPolar: function(p) { 45 | return { 46 | angle: Math.atan2(p.y, p.x), 47 | radius: this.magnitude(p) 48 | }; 49 | }, 50 | 51 | magnitude: function(p) { 52 | return this.dist(0, 0, p.x, p.y); 53 | }, 54 | 55 | dist: function (x0, y0, x1, y1) { 56 | if(arguments.length === 2) { 57 | return this.dist(x0.x, x0.y, y0.x, y0.y); 58 | } 59 | var dx = x1 - x0, 60 | dy = y1 - y0; 61 | return Math.sqrt(dx * dx + dy * dy); 62 | }, 63 | 64 | lerpPoint: function(p0, p1, t) { 65 | return { 66 | x: this.lerp(p0.x, p1.x, t), 67 | y: this.lerp(p0.y, p1.y, t) 68 | }; 69 | }, 70 | 71 | bezier: function(p0, p1, p2, p3, t) { 72 | var oneMinusT = 1 - t, 73 | m0 = oneMinusT * oneMinusT * oneMinusT, 74 | m1 = 3 * oneMinusT * oneMinusT * t, 75 | m2 = 3 * oneMinusT * t * t, 76 | m3 = t * t * t; 77 | return { 78 | x: m0 * p0.x + m1 * p1.x + m2 * p2.x + m3 * p3.x, 79 | y: m0 * p0.y + m1 * p1.y + m2 * p2.y + m3 * p3.y 80 | }; 81 | }, 82 | 83 | quadratic: function(p0, p1, p2, t) { 84 | var oneMinusT = 1 - t, 85 | m0 = oneMinusT * oneMinusT, 86 | m1 = 2 * oneMinusT * t, 87 | m2 = t * t; 88 | return { 89 | x: m0 * p0.x + m1 * p1.x + m2 * p2.x, 90 | y: m0 * p0.y + m1 * p1.y + m2 * p2.y 91 | } 92 | 93 | }, 94 | 95 | pointInCircle: function(px, py, cx, cy, cr) { 96 | var dist = this.dist(px, py, cx, cy); 97 | return dist <= cr; 98 | }, 99 | 100 | pointInRect: function(px, py, rx, ry, rw, rh) { 101 | return px >= rx && 102 | py >= ry && 103 | px <= rx + rw && 104 | py <= ry + rh; 105 | }, 106 | 107 | segmentIntersect: function(p0, p1, p2, p3) { 108 | var A1 = p1.y - p0.y, 109 | B1 = p0.x - p1.x, 110 | C1 = A1 * p0.x + B1 * p0.y, 111 | A2 = p3.y - p2.y, 112 | B2 = p2.x - p3.x, 113 | C2 = A2 * p2.x + B2 * p2.y, 114 | denominator = A1 * B2 - A2 * B1; 115 | 116 | if (denominator == 0) { 117 | return null; 118 | } 119 | 120 | var intersectX = (B2 * C1 - B1 * C2) / denominator, 121 | intersectY = (A1 * C2 - A2 * C1) / denominator, 122 | rx0 = (intersectX - p0.x) / (p1.x - p0.x), 123 | ry0 = (intersectY - p0.y) / (p1.y - p0.y), 124 | rx1 = (intersectX - p2.x) / (p3.x - p2.x), 125 | ry1 = (intersectY - p2.y) / (p3.y - p2.y); 126 | 127 | if (((rx0 >= 0 && rx0 <= 1) || (ry0 >= 0 && ry0 <= 1)) && 128 | ((rx1 >= 0 && rx1 <= 1) || (ry1 >= 0 && ry1 <= 1))) { 129 | return { 130 | x: intersectX, 131 | y: intersectY 132 | }; 133 | } 134 | else { 135 | return null; 136 | } 137 | }, 138 | 139 | tangentPointToCircle: function(x, y, cx, cy, cr, anticlockwise) { 140 | var dist = bitlib.math.dist(x, y, cx, cy), 141 | dir = anticlockwise ? 1 : -1, 142 | angle = Math.acos(-cr / dist) * dir, 143 | baseAngle = Math.atan2(cy - y, cx - x), 144 | totalAngle = baseAngle + angle; 145 | 146 | return { 147 | x: cx + Math.cos(totalAngle) * cr, 148 | y: cy + Math.sin(totalAngle) * cr 149 | }; 150 | } 151 | }; 152 | 153 | 154 | bitlib.context = function (w, h, parent) { 155 | if(w === 0 || h === 0) { 156 | w = window.innerWidth; 157 | h = window.innerHeight; 158 | } 159 | var canvas = document.createElement("canvas"); 160 | var context = canvas.getContext("2d"); 161 | bitlib.extendContext(context); 162 | canvas.style.display = "block"; 163 | context.setSize(w || 600, h || 600); 164 | document.body.style.margin = "0"; 165 | document.body.style.padding = "0"; 166 | parent = parent || document.body; 167 | parent.appendChild(canvas); 168 | return context; 169 | }; 170 | 171 | bitlib.extendContext = function(context) { 172 | 173 | context.setShadow =function(color, offsetX, offsetY, blur) { 174 | if(typeof color === "number") { 175 | this.shadowColor = bitlib.color.rgba(0,0,0, color); 176 | } 177 | else { 178 | this.shadowColor = color; 179 | } 180 | this.shadowOffsetX = offsetX; 181 | this.shadowOffsetY = offsetY; 182 | this.shadowBlur = blur; 183 | }; 184 | 185 | context.clear = function(color) { 186 | this.save(); 187 | this.setTransform(1, 0, 0, 1, 0, 0); 188 | if(color) { 189 | this.fillStyle = color; 190 | this.fillRect(0, 0, this.canvas.width, this.canvas.height); 191 | } 192 | else { 193 | this.clearRect(0, 0, this.canvas.width, this.canvas.height); 194 | } 195 | this.restore(); 196 | } 197 | 198 | context.circle = function(x, y, radius) { 199 | this.arc(x, y, radius, 0, Math.PI * 2); 200 | } 201 | 202 | context.fillCircle = function(x, y, radius) { 203 | this.beginPath(); 204 | this.circle(x, y, radius); 205 | this.fill(); 206 | } 207 | 208 | context.strokeCircle = function(x, y, radius) { 209 | this.beginPath(); 210 | this.circle(x, y, radius); 211 | this.stroke(); 212 | } 213 | 214 | context.ellipse = function(x, y, xr, yr) { 215 | this.save(); 216 | this.translate(x, y); 217 | this.scale(xr, yr); 218 | this.arc(0, 0, 1, 0, Math.PI * 2); 219 | this.restore(); 220 | } 221 | 222 | context.setSize = function(w, h) { 223 | this.width = this.canvas.width = w; 224 | this.height = this.canvas.height = h; 225 | }; 226 | 227 | context.multiCurve = function(points) { 228 | this.moveTo(points[0].x, points[0].y); 229 | this.lineTo((points[0].x + points[1].x) / 2, (points[0].y + points[1].y) / 2); 230 | for(var i = 1; i < points.length - 1; i++) { 231 | var p0 = points[i], 232 | p1 = points[i + 1], 233 | midx = (p0.x + p1.x) / 2, 234 | midy = (p0.y + p1.y) / 2; 235 | this.quadraticCurveTo(p0.x, p0.y, midx, midy); 236 | 237 | } 238 | var p = points[points.length - 1]; 239 | this.lineTo(p.x, p.y); 240 | }; 241 | 242 | context.strokeMultiCurve = function(points) { 243 | this.beginPath(); 244 | this.multiCurve(points); 245 | this.stroke(); 246 | }; 247 | 248 | context.multiLoop = function(points) { 249 | var pA = points[0], 250 | pB = points[1], 251 | pZ = points[points.length - 1], 252 | mid1x = (pZ.x + pA.x) / 2, 253 | mid1y = (pZ.y + pA.y) / 2; 254 | this.moveTo(mid1x, mid1y); 255 | for(var i = 0; i < points.length - 1; i++) { 256 | var p0 = points[i], 257 | p1 = points[i + 1], 258 | midx = (p0.x + p1.x) / 2, 259 | midy = (p0.y + p1.y) / 2; 260 | this.quadraticCurveTo(p0.x, p0.y, midx, midy); 261 | } 262 | this.quadraticCurveTo(pZ.x, pZ.y, mid1x, mid1y); 263 | }; 264 | 265 | context.strokeMultiLoop = function(points) { 266 | this.beginPath(); 267 | this.multiLoop(points); 268 | this.stroke(); 269 | }; 270 | 271 | context.fillMultiLoop = function(points) { 272 | this.beginPath(); 273 | this.multiLoop(points); 274 | this.fill(); 275 | }; 276 | 277 | context.path = function(points) { 278 | context.moveTo(points[0].x, points[0].y); 279 | for(var i = 1; i < points.length; i++) { 280 | context.lineTo(points[i].x, points[i].y); 281 | } 282 | }; 283 | 284 | context.strokePath = function(points, close) { 285 | context.beginPath(); 286 | context.path(points); 287 | if(close) { 288 | context.closePath(); 289 | } 290 | context.stroke(); 291 | }; 292 | 293 | context.fillPath = function(points) { 294 | context.beginPath(); 295 | context.path(points); 296 | context.fill(); 297 | }; 298 | 299 | context.line = function(x0, y0, x1, y1) { 300 | context.beginPath(); 301 | context.moveTo(x0, y0); 302 | context.lineTo(x1, y1); 303 | context.stroke(); 304 | }; 305 | 306 | 307 | 308 | 309 | 310 | }; 311 | 312 | bitlib.color = { 313 | rgb: function(r, g, b) { 314 | return this.rgba(r, g, b, 1); 315 | }, 316 | 317 | rgba: function(r, g, b, a) { 318 | return this.Color.create(r, g, b, a).toString(); 319 | }, 320 | 321 | rgbf: function(r, g, b) { 322 | return this.rgbaf(r, g, b, 1); 323 | }, 324 | 325 | rgbaf: function(r, g, b, a) { 326 | return this.rgb(r * 255, g * 255, b * 255, a); 327 | }, 328 | 329 | number: function(num) { 330 | return this.rgb(num >> 16, num >> 8 & 0xff, num & 0xff); 331 | }, 332 | 333 | randomRGB: function() { 334 | return this.number(bitlib.random.int(0xffffff)); 335 | }, 336 | 337 | gray: function(shade) { 338 | return this.rgb(shade, shade, shade); 339 | }, 340 | 341 | randomGray: function() { 342 | return this.gray(bitlib.random.int(255)); 343 | }, 344 | 345 | hsv: function(h, s, v) { 346 | h /= 360; 347 | var r, g, b, 348 | i = Math.floor(h * 6), 349 | f = h * 6 - i, 350 | p = v * (1 - s), 351 | q = v * (1 - f * s), 352 | t = v * (1 - (1 - f) * s); 353 | switch (i % 6) { 354 | case 0: r = v, g = t, b = p; break; 355 | case 1: r = q, g = v, b = p; break; 356 | case 2: r = p, g = v, b = t; break; 357 | case 3: r = p, g = q, b = v; break; 358 | case 4: r = t, g = p, b = v; break; 359 | case 5: r = v, g = p, b = q; break; 360 | } 361 | return this.rgb(r * 255, g * 255, b * 255); 362 | }, 363 | 364 | lerp: function(t, colorA, colorB) { 365 | var ca, cb; 366 | if(typeof colorA === "string") { 367 | ca = this.string(colorA); 368 | } 369 | else if(typeof colorA === "number") { 370 | ca = this.num(colorA); 371 | } 372 | else if(colorA.isColorObject) { 373 | ca = colorA; 374 | } 375 | if(typeof colorB === "string") { 376 | cb = this.string(colorB); 377 | } 378 | else if(typeof colorB === "number") { 379 | cb = this.number(colorB); 380 | } 381 | else if(colorB.isColorObject) { 382 | cb = colorB; 383 | } 384 | var r = bitlib.math._lerp(ca.red, cb.red, t), 385 | g = bitlib.math._lerp(ca.green, cb.green, t), 386 | b = bitlib.math._lerp(ca.blue, cb.blue, t), 387 | a = bitlib.math._lerp(ca.alpha, cb.alpha, t); 388 | return this.rgba(r, g, b, a); 389 | }, 390 | 391 | string: function(str) { 392 | if(str.charAt(0) === "#" && str.length === 7) { 393 | str = "0x" + str.substr(1); 394 | var num = parseInt(str, 16); 395 | return this.number(num); 396 | } 397 | else if(str.charAt(0) === "#" && str.length === 4) { 398 | var r = str.charAt(1), 399 | g = str.charAt(2), 400 | b = str.charAt(3); 401 | str = "0x" + r + r + g + g + b + b; 402 | var num = parseInt(str, 16); 403 | return this.number(num); 404 | } 405 | else if(str.indexOf("rgba(") === 0) { 406 | var vals = str.substring(5, str.length - 1).split(","); 407 | return this.rgba( 408 | parseInt(vals[0], 10), 409 | parseInt(vals[1], 10), 410 | parseInt(vals[2], 10), 411 | parseFloat(vals[3]) 412 | ); 413 | } 414 | else if(str.indexOf("rgb(") === 0) { 415 | var vals = str.substring(4, str.length - 1).split(","); 416 | return this.rgba( 417 | parseInt(vals[0], 10), 418 | parseInt(vals[1], 10), 419 | parseInt(vals[2], 10), 420 | 1 421 | ); 422 | } 423 | else if(this._colorMap[str]) { 424 | return this.rgba( 425 | this._colorMap[str][0], 426 | this._colorMap[str][1], 427 | this._colorMap[str][2], 428 | 1 429 | ); 430 | } 431 | else { 432 | return this.rgba(0, 0, 0, 1); 433 | } 434 | }, 435 | 436 | _colorMap: { 437 | blueviolet: [138,43,226], 438 | brown: [165,42,42], 439 | aliceblue: [240,248,255], 440 | antiquewhite: [250,235,215], 441 | aqua: [0,255,255], 442 | aquamarine: [127,255,212], 443 | azure: [240,255,255], 444 | beige: [245,245,220], 445 | bisque: [255,228,196], 446 | black: [0,0,0], 447 | blanchedalmond: [255,235,205], 448 | blue: [0,0,255], 449 | burlywood: [222,184,135], 450 | cadetblue: [95,158,160], 451 | chartreuse: [127,255,0], 452 | chocolate: [210,105,30], 453 | coral: [255,127,80], 454 | cornflowerblue: [100,149,237], 455 | cornsilk: [255,248,220], 456 | crimson: [220,20,60], 457 | cyan: [0,255,255], 458 | darkblue: [0,0,139], 459 | darkcyan: [0,139,139], 460 | darkgoldenrod: [184,134,11], 461 | darkgray: [169,169,169], 462 | darkgreen: [0,100,0], 463 | darkgrey: [169,169,169], 464 | darkkhaki: [189,183,107], 465 | darkmagenta: [139,0,139], 466 | darkolivegreen: [85,107,47], 467 | darkorange: [255,140,0], 468 | darkorchid: [153,50,204], 469 | darkred: [139,0,0], 470 | darksalmon: [233,150,122], 471 | darkseagreen: [143,188,143], 472 | darkslateblue: [72,61,139], 473 | darkslategray: [47,79,79], 474 | darkslategrey: [47,79,79], 475 | darkturquoise: [0,206,209], 476 | darkviolet: [148,0,211], 477 | deeppink: [255,20,147], 478 | deepskyblue: [0,191,255], 479 | dimgray: [105,105,105], 480 | dimgrey: [105,105,105], 481 | dodgerblue: [30,144,255], 482 | firebrick: [178,34,34], 483 | floralwhite: [255,250,240], 484 | forestgreen: [34,139,34], 485 | fuchsia: [255,0,255], 486 | gainsboro: [220,220,220], 487 | ghostwhite: [248,248,255], 488 | gold: [255,215,0], 489 | goldenrod: [218,165,32], 490 | gray: [128,128,128], 491 | green: [0,128,0], 492 | greenyellow: [173,255,47], 493 | grey: [128,128,128], 494 | honeydew: [240,255,240], 495 | hotpink: [255,105,180], 496 | indianred: [205,92,92], 497 | indigo: [75,0,130], 498 | ivory: [255,255,240], 499 | khaki: [240,230,140], 500 | lavender: [230,230,250], 501 | lavenderblush: [255,240,245], 502 | lawngreen: [124,252,0], 503 | lemonchiffon: [255,250,205], 504 | lightblue: [173,216,230], 505 | lightcoral: [240,128,128], 506 | lightcyan: [224,255,255], 507 | lightgoldenrodyellow: [250,250,210], 508 | lightgray: [211,211,211], 509 | lightgreen: [144,238,144], 510 | lightgrey: [211,211,211], 511 | lightpink: [255,182,193], 512 | lightsalmon: [255,160,122], 513 | lightseagreen: [32,178,170], 514 | lightskyblue: [135,206,250], 515 | lightslategray: [119,136,153], 516 | lightslategrey: [119,136,153], 517 | lightsteelblue: [176,196,222], 518 | lightyellow: [255,255,224], 519 | lime: [0,255,0], 520 | limegreen: [50,205,50], 521 | linen: [250,240,230], 522 | magenta: [255,0,255], 523 | maroon: [128,0,0], 524 | mediumaquamarine: [102,205,170], 525 | mediumblue: [0,0,205], 526 | mediumorchid: [186,85,211], 527 | mediumpurple: [147,112,219], 528 | mediumseagreen: [60,179,113], 529 | mediumslateblue: [123,104,238], 530 | mediumspringgreen: [0,250,154], 531 | mediumturquoise: [72,209,204], 532 | mediumvioletred: [199,21,133], 533 | midnightblue: [25,25,112], 534 | mintcream: [245,255,250], 535 | mistyrose: [255,228,225], 536 | moccasin: [255,228,181], 537 | navajowhite: [255,222,173], 538 | navy: [0,0,128], 539 | oldlace: [253,245,230], 540 | olive: [128,128,0], 541 | olivedrab: [107,142,35], 542 | orange: [255,165,0], 543 | orangered: [255,69,0], 544 | orchid: [218,112,214], 545 | palegoldenrod: [238,232,170], 546 | palegreen: [152,251,152], 547 | paleturquoise: [175,238,238], 548 | palevioletred: [219,112,147], 549 | papayawhip: [255,239,213], 550 | peachpuff: [255,218,185], 551 | peru: [205,133,63], 552 | pink: [255,192,203], 553 | plum: [221,160,221], 554 | powderblue: [176,224,230], 555 | purple: [128,0,128], 556 | rebeccapurple: [102,51,153], 557 | red: [255,0,0], 558 | rosybrown: [188,143,143], 559 | royalblue: [65,105,225], 560 | saddlebrown: [139,69,19], 561 | salmon: [250,128,114], 562 | sandybrown: [244,164,96], 563 | seagreen: [46,139,87], 564 | seashell: [255,245,238], 565 | sienna: [160,82,45], 566 | silver: [192,192,192], 567 | skyblue: [135,206,235], 568 | slateblue: [106,90,205], 569 | slategray: [112,128,144], 570 | slategrey: [112,128,144], 571 | snow: [255,250,250], 572 | springgreen: [0,255,127], 573 | steelblue: [70,130,180], 574 | tan: [210,180,140], 575 | teal: [0,128,128], 576 | thistle: [216,191,216], 577 | tomato: [255,99,71], 578 | turquoise: [64,224,208], 579 | violet: [238,130,238], 580 | wheat: [245,222,179], 581 | white: [255,255,255], 582 | whitesmoke: [245,245,245], 583 | yellow: [255,255,0], 584 | yellowgreen: [154,205,50] 585 | }, 586 | 587 | Color: { 588 | isColorObject: true, 589 | 590 | create: function(r, g, b, a) { 591 | var obj = Object.create(this); 592 | obj._init(r, g, b, a); 593 | return obj; 594 | }, 595 | 596 | _init: function(r, g, b, a) { 597 | this.red = r; 598 | this.green = g; 599 | this.blue = b; 600 | this.alpha = a; 601 | }, 602 | 603 | toString: function () { 604 | return "rgba(" + Math.floor(this.red) + "," + Math.floor(this.green) + "," + Math.floor(this.blue) + "," + this.alpha + ")"; 605 | } 606 | } 607 | 608 | }; 609 | 610 | bitlib.random = { 611 | _seed: Date.now(), 612 | _a: 1664525, 613 | _c: 1013904223, 614 | _m: Math.pow(2, 32), 615 | 616 | seed: function(seed) { 617 | this._seed = seed; 618 | }, 619 | 620 | _int: function() { 621 | // range [0, 2^32) 622 | this._seed = (this._seed * this._a + this._c) % this._m; 623 | return this._seed; 624 | }, 625 | 626 | _float: function() { 627 | // range [0, 1) 628 | return this._int() / this._m; 629 | }, 630 | 631 | bool: function(percent) { 632 | // percent is chance of getting true 633 | if(percent == null) { 634 | percent = 0.5; 635 | } 636 | return this._float() < percent; 637 | }, 638 | 639 | float: function(min, max) { 640 | // range [min, max) 641 | if(arguments.length === 1) { 642 | return this._float() * min; 643 | } 644 | if(arguments.length === 2) { 645 | return min + this._float() * (max - min); 646 | } 647 | return this._float(); 648 | }, 649 | 650 | int: function(min, max) { 651 | // range [min, max) 652 | if(arguments.length === 1) { 653 | return Math.floor(this._float() * min); 654 | } 655 | if(arguments.length === 2) { 656 | return Math.floor(this.float(min, max)); 657 | } 658 | return this._int(); 659 | }, 660 | 661 | power: function(min, max, power) { 662 | if(arguments.length === 2) { 663 | power = max; 664 | max = min; 665 | min = 0; 666 | } 667 | return min + Math.pow(this.float(1), power) * (max - min); 668 | }, 669 | 670 | powerInt: function(min, max, power) { 671 | return Math.floor(this.power(min, max, power)); 672 | }, 673 | 674 | gauss: function(min, max, g) { 675 | if(arguments.length === 2) { 676 | g = max; 677 | max = min; 678 | min = 0; 679 | } 680 | var total = 0; 681 | for(var i = 0; i < g; i++) { 682 | total += this.float(min, max); 683 | } 684 | return total / g; 685 | }, 686 | 687 | chooser: function() { 688 | 689 | return { 690 | choices: [], 691 | total: 0, 692 | 693 | addChoice: function (choice, weight) { 694 | if (weight == null) weight = 1; 695 | 696 | this.choices.push({ 697 | weight: weight, 698 | choice: choice 699 | }); 700 | this.total += weight; 701 | return this; 702 | }, 703 | 704 | getChoice: function () { 705 | var rand = bitlib.random.float(0, this.total); 706 | for (var i = 0; i < this.choices.length; i++) { 707 | var choice = this.choices[i]; 708 | if (rand < choice.weight) { 709 | return choice.choice; 710 | } 711 | rand -= choice.weight; 712 | } 713 | } 714 | } 715 | } 716 | }; 717 | 718 | 719 | bitlib.anim = function(renderCallback, fps) { 720 | 721 | return { 722 | fps: fps || 60, 723 | renderCallback: renderCallback, 724 | 725 | start: function () { 726 | if (!this.running) { 727 | this.running = true; 728 | this.render(); 729 | } 730 | this.shouldKill = false; 731 | return this; 732 | }, 733 | 734 | stop: function () { 735 | this.shouldKill = true; 736 | return this; 737 | }, 738 | 739 | toggle: function () { 740 | if (this.running) { 741 | this.stop(); 742 | } 743 | else { 744 | this.start(); 745 | } 746 | return this; 747 | }, 748 | 749 | render: function () { 750 | if(this.shouldKill) { 751 | this.shouldKill = false; 752 | this.running = false; 753 | } 754 | if (this.running) { 755 | if (this.renderCallback) { 756 | this.renderCallback(); 757 | } 758 | var self = this; 759 | setTimeout(function () { 760 | requestAnimationFrame(function () { 761 | self.render(); 762 | }); 763 | }, 1000 / this.fps); 764 | } 765 | } 766 | }; 767 | } 768 | 769 | bitlib.image = function(url, callback) { 770 | var img = document.createElement("img"); 771 | img.addEventListener("load", function() { 772 | callback(img); 773 | }); 774 | img.src = url; 775 | return img; 776 | } 777 | 778 | bitlib.imageData = function(url, callback) { 779 | var image = bitlib.image(url, function() { 780 | var w = image.width, 781 | h = image.height, 782 | canvas = document.createElement("canvas"), 783 | context = canvas.getContext("2d"); 784 | canvas.width = w; 785 | canvas.height = h; 786 | context.drawImage(image, 0, 0); 787 | try { 788 | callback(context.getImageData(0, 0, w, h)); 789 | } 790 | catch(e) { 791 | console.log(e.message); 792 | callback(null); 793 | } 794 | }); 795 | } 796 | 797 | if (typeof define === "function" && define.amd) { 798 | define(bitlib); 799 | } else { 800 | if(window.bitlib) { 801 | for(var prop in bitlib) { 802 | window.bitlib[prop] = bitlib[prop]; 803 | } 804 | } 805 | else { 806 | window.bitlib = bitlib; 807 | } 808 | } 809 | 810 | })(); -------------------------------------------------------------------------------- /bitlib.min.js: -------------------------------------------------------------------------------- 1 | !function(){var a={};if(a.math={norm:function(a,b,c){return(a-b)/(c-b)},lerp:function(a,b,c){return a+(b-a)*c},map:function(a,b,c,d,e){var f=this.norm(a,b,c);return this.lerp(d,e,f)},clamp:function(a,b,c){return Math.min(Math.max(a,b),c)},dotProduct:function(a,b,c,d,e,f,g,h){var i=c-a,j=d-b,k=g-e,l=h-f;return i*k+j*l},angleBetween:function(a,b,c,d,e,f,g,h){var i=this.dotProduct(a,b,c,d,e,f,g,h),j=this.dist(a,b,c,d),k=this.dist(e,f,g,h);return Math.acos(i/j/k)},polarToPoint:function(a,b){return{x:Math.cos(a)*b,y:Math.sin(a)*b}},pointToPolar:function(a){return{angle:Math.atan2(a.y,a.x),radius:this.magnitude(a)}},magnitude:function(a){return this.dist(0,0,a.x,a.y)},dist:function(a,b,c,d){if(2===arguments.length)return this.dist(a.x,a.y,b.x,b.y);var e=c-a,f=d-b;return Math.sqrt(e*e+f*f)},lerpPoint:function(a,b,c){return{x:this.lerp(a.x,b.x,c),y:this.lerp(a.y,b.y,c)}},bezier:function(a,b,c,d,e){var f=1-e,g=f*f*f,h=3*f*f*e,i=3*f*e*e,j=e*e*e;return{x:g*a.x+h*b.x+i*c.x+j*d.x,y:g*a.y+h*b.y+i*c.y+j*d.y}},quadratic:function(a,b,c,d){var e=1-d,f=e*e,g=2*e*d,h=d*d;return{x:f*a.x+g*b.x+h*c.x,y:f*a.y+g*b.y+h*c.y}},pointInCircle:function(a,b,c,d,e){var f=this.dist(a,b,c,d);return f<=e},pointInRect:function(a,b,c,d,e,f){return a>=c&&b>=d&&a<=c+e&&b<=d+f},segmentIntersect:function(a,b,c,d){var e=b.y-a.y,f=a.x-b.x,g=e*a.x+f*a.y,h=d.y-c.y,i=c.x-d.x,j=h*c.x+i*c.y,k=e*i-h*f;if(0==k)return null;var l=(i*g-f*j)/k,m=(e*j-h*g)/k,n=(l-a.x)/(b.x-a.x),o=(m-a.y)/(b.y-a.y),p=(l-c.x)/(d.x-c.x),q=(m-c.y)/(d.y-c.y);return(n>=0&&n<=1||o>=0&&o<=1)&&(p>=0&&p<=1||q>=0&&q<=1)?{x:l,y:m}:null},tangentPointToCircle:function(b,c,d,e,f,g){var h=a.math.dist(b,c,d,e),i=g?1:-1,j=Math.acos(-f/h)*i,k=Math.atan2(e-c,d-b),l=k+j;return{x:d+Math.cos(l)*f,y:e+Math.sin(l)*f}}},a.context=function(b,c,d){0!==b&&0!==c||(b=window.innerWidth,c=window.innerHeight);var e=document.createElement("canvas"),f=e.getContext("2d");return a.extendContext(f),e.style.display="block",f.setSize(b||600,c||600),document.body.style.margin="0",document.body.style.padding="0",d=d||document.body,d.appendChild(e),f},a.extendContext=function(b){b.setShadow=function(b,c,d,e){"number"==typeof b?this.shadowColor=a.color.rgba(0,0,0,b):this.shadowColor=b,this.shadowOffsetX=c,this.shadowOffsetY=d,this.shadowBlur=e},b.clear=function(a){this.save(),this.setTransform(1,0,0,1,0,0),a?(this.fillStyle=a,this.fillRect(0,0,this.canvas.width,this.canvas.height)):this.clearRect(0,0,this.canvas.width,this.canvas.height),this.restore()},b.circle=function(a,b,c){this.arc(a,b,c,0,2*Math.PI)},b.fillCircle=function(a,b,c){this.beginPath(),this.circle(a,b,c),this.fill()},b.strokeCircle=function(a,b,c){this.beginPath(),this.circle(a,b,c),this.stroke()},b.ellipse=function(a,b,c,d){this.save(),this.translate(a,b),this.scale(c,d),this.arc(0,0,1,0,2*Math.PI),this.restore()},b.setSize=function(a,b){this.width=this.canvas.width=a,this.height=this.canvas.height=b},b.multiCurve=function(a){this.moveTo(a[0].x,a[0].y),this.lineTo((a[0].x+a[1].x)/2,(a[0].y+a[1].y)/2);for(var b=1;b>16,a>>8&255,255&a)},randomRGB:function(){return this.number(a.random.int(16777215))},gray:function(a){return this.rgb(a,a,a)},randomGray:function(){return this.gray(a.random.int(255))},hsv:function(a,b,c){a/=360;var d,e,f,g=Math.floor(6*a),h=6*a-g,i=c*(1-b),j=c*(1-h*b),k=c*(1-(1-h)*b);switch(g%6){case 0:d=c,e=k,f=i;break;case 1:d=j,e=c,f=i;break;case 2:d=i,e=c,f=k;break;case 3:d=i,e=j,f=c;break;case 4:d=k,e=i,f=c;break;case 5:d=c,e=i,f=j}return this.rgb(255*d,255*e,255*f)},lerp:function(b,c,d){var e,f;"string"==typeof c?e=this.string(c):"number"==typeof c?e=this.num(c):c.isColorObject&&(e=c),"string"==typeof d?f=this.string(d):"number"==typeof d?f=this.number(d):d.isColorObject&&(f=d);var g=a.math._lerp(e.red,f.red,b),h=a.math._lerp(e.green,f.green,b),i=a.math._lerp(e.blue,f.blue,b),j=a.math._lerp(e.alpha,f.alpha,b);return this.rgba(g,h,i,j)},string:function(a){if("#"===a.charAt(0)&&7===a.length){a="0x"+a.substr(1);var b=parseInt(a,16);return this.number(b)}if("#"===a.charAt(0)&&4===a.length){var c=a.charAt(1),d=a.charAt(2),e=a.charAt(3);a="0x"+c+c+d+d+e+e;var b=parseInt(a,16);return this.number(b)}if(0===a.indexOf("rgba(")){var f=a.substring(5,a.length-1).split(",");return this.rgba(parseInt(f[0],10),parseInt(f[1],10),parseInt(f[2],10),parseFloat(f[3]))}if(0===a.indexOf("rgb(")){var f=a.substring(4,a.length-1).split(",");return this.rgba(parseInt(f[0],10),parseInt(f[1],10),parseInt(f[2],10),1)}return this._colorMap[a]?this.rgba(this._colorMap[a][0],this._colorMap[a][1],this._colorMap[a][2],1):this.rgba(0,0,0,1)},_colorMap:{blueviolet:[138,43,226],brown:[165,42,42],aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},Color:{isColorObject:!0,create:function(a,b,c,d){var e=Object.create(this);return e._init(a,b,c,d),e},_init:function(a,b,c,d){this.red=a,this.green=b,this.blue=c,this.alpha=d},toString:function(){return"rgba("+Math.floor(this.red)+","+Math.floor(this.green)+","+Math.floor(this.blue)+","+this.alpha+")"}}},a.random={_seed:Date.now(),_a:1664525,_c:1013904223,_m:Math.pow(2,32),seed:function(a){this._seed=a},_int:function(){return this._seed=(this._seed*this._a+this._c)%this._m,this._seed},_float:function(){return this._int()/this._m},bool:function(a){return null==a&&(a=.5),this._float()> 16, num >> 8 & 0xff, num & 0xff); 20 | }, 21 | 22 | randomRGB: function() { 23 | return this.number(bitlib.random.int(0xffffff)); 24 | }, 25 | 26 | gray: function(shade) { 27 | return this.rgb(shade, shade, shade); 28 | }, 29 | 30 | randomGray: function() { 31 | return this.gray(bitlib.random.int(255)); 32 | }, 33 | 34 | hsv: function(h, s, v) { 35 | h /= 360; 36 | var r, g, b, 37 | i = Math.floor(h * 6), 38 | f = h * 6 - i, 39 | p = v * (1 - s), 40 | q = v * (1 - f * s), 41 | t = v * (1 - (1 - f) * s); 42 | switch (i % 6) { 43 | case 0: r = v, g = t, b = p; break; 44 | case 1: r = q, g = v, b = p; break; 45 | case 2: r = p, g = v, b = t; break; 46 | case 3: r = p, g = q, b = v; break; 47 | case 4: r = t, g = p, b = v; break; 48 | case 5: r = v, g = p, b = q; break; 49 | } 50 | return this.rgb(r * 255, g * 255, b * 255); 51 | }, 52 | 53 | lerp: function(t, colorA, colorB) { 54 | var ca, cb; 55 | if(typeof colorA === "string") { 56 | ca = this.string(colorA); 57 | } 58 | else if(typeof colorA === "number") { 59 | ca = this.num(colorA); 60 | } 61 | else if(colorA.isColorObject) { 62 | ca = colorA; 63 | } 64 | if(typeof colorB === "string") { 65 | cb = this.string(colorB); 66 | } 67 | else if(typeof colorB === "number") { 68 | cb = this.number(colorB); 69 | } 70 | else if(colorB.isColorObject) { 71 | cb = colorB; 72 | } 73 | var r = bitlib.math._lerp(ca.red, cb.red, t), 74 | g = bitlib.math._lerp(ca.green, cb.green, t), 75 | b = bitlib.math._lerp(ca.blue, cb.blue, t), 76 | a = bitlib.math._lerp(ca.alpha, cb.alpha, t); 77 | return this.rgba(r, g, b, a); 78 | }, 79 | 80 | string: function(str) { 81 | if(str.charAt(0) === "#" && str.length === 7) { 82 | str = "0x" + str.substr(1); 83 | var num = parseInt(str, 16); 84 | return this.number(num); 85 | } 86 | else if(str.charAt(0) === "#" && str.length === 4) { 87 | var r = str.charAt(1), 88 | g = str.charAt(2), 89 | b = str.charAt(3); 90 | str = "0x" + r + r + g + g + b + b; 91 | var num = parseInt(str, 16); 92 | return this.number(num); 93 | } 94 | else if(str.indexOf("rgba(") === 0) { 95 | var vals = str.substring(5, str.length - 1).split(","); 96 | return this.rgba( 97 | parseInt(vals[0], 10), 98 | parseInt(vals[1], 10), 99 | parseInt(vals[2], 10), 100 | parseFloat(vals[3]) 101 | ); 102 | } 103 | else if(str.indexOf("rgb(") === 0) { 104 | var vals = str.substring(4, str.length - 1).split(","); 105 | return this.rgba( 106 | parseInt(vals[0], 10), 107 | parseInt(vals[1], 10), 108 | parseInt(vals[2], 10), 109 | 1 110 | ); 111 | } 112 | else if(this._colorMap[str]) { 113 | return this.rgba( 114 | this._colorMap[str][0], 115 | this._colorMap[str][1], 116 | this._colorMap[str][2], 117 | 1 118 | ); 119 | } 120 | else { 121 | return this.rgba(0, 0, 0, 1); 122 | } 123 | }, 124 | 125 | _colorMap: { 126 | blueviolet: [138,43,226], 127 | brown: [165,42,42], 128 | aliceblue: [240,248,255], 129 | antiquewhite: [250,235,215], 130 | aqua: [0,255,255], 131 | aquamarine: [127,255,212], 132 | azure: [240,255,255], 133 | beige: [245,245,220], 134 | bisque: [255,228,196], 135 | black: [0,0,0], 136 | blanchedalmond: [255,235,205], 137 | blue: [0,0,255], 138 | burlywood: [222,184,135], 139 | cadetblue: [95,158,160], 140 | chartreuse: [127,255,0], 141 | chocolate: [210,105,30], 142 | coral: [255,127,80], 143 | cornflowerblue: [100,149,237], 144 | cornsilk: [255,248,220], 145 | crimson: [220,20,60], 146 | cyan: [0,255,255], 147 | darkblue: [0,0,139], 148 | darkcyan: [0,139,139], 149 | darkgoldenrod: [184,134,11], 150 | darkgray: [169,169,169], 151 | darkgreen: [0,100,0], 152 | darkgrey: [169,169,169], 153 | darkkhaki: [189,183,107], 154 | darkmagenta: [139,0,139], 155 | darkolivegreen: [85,107,47], 156 | darkorange: [255,140,0], 157 | darkorchid: [153,50,204], 158 | darkred: [139,0,0], 159 | darksalmon: [233,150,122], 160 | darkseagreen: [143,188,143], 161 | darkslateblue: [72,61,139], 162 | darkslategray: [47,79,79], 163 | darkslategrey: [47,79,79], 164 | darkturquoise: [0,206,209], 165 | darkviolet: [148,0,211], 166 | deeppink: [255,20,147], 167 | deepskyblue: [0,191,255], 168 | dimgray: [105,105,105], 169 | dimgrey: [105,105,105], 170 | dodgerblue: [30,144,255], 171 | firebrick: [178,34,34], 172 | floralwhite: [255,250,240], 173 | forestgreen: [34,139,34], 174 | fuchsia: [255,0,255], 175 | gainsboro: [220,220,220], 176 | ghostwhite: [248,248,255], 177 | gold: [255,215,0], 178 | goldenrod: [218,165,32], 179 | gray: [128,128,128], 180 | green: [0,128,0], 181 | greenyellow: [173,255,47], 182 | grey: [128,128,128], 183 | honeydew: [240,255,240], 184 | hotpink: [255,105,180], 185 | indianred: [205,92,92], 186 | indigo: [75,0,130], 187 | ivory: [255,255,240], 188 | khaki: [240,230,140], 189 | lavender: [230,230,250], 190 | lavenderblush: [255,240,245], 191 | lawngreen: [124,252,0], 192 | lemonchiffon: [255,250,205], 193 | lightblue: [173,216,230], 194 | lightcoral: [240,128,128], 195 | lightcyan: [224,255,255], 196 | lightgoldenrodyellow: [250,250,210], 197 | lightgray: [211,211,211], 198 | lightgreen: [144,238,144], 199 | lightgrey: [211,211,211], 200 | lightpink: [255,182,193], 201 | lightsalmon: [255,160,122], 202 | lightseagreen: [32,178,170], 203 | lightskyblue: [135,206,250], 204 | lightslategray: [119,136,153], 205 | lightslategrey: [119,136,153], 206 | lightsteelblue: [176,196,222], 207 | lightyellow: [255,255,224], 208 | lime: [0,255,0], 209 | limegreen: [50,205,50], 210 | linen: [250,240,230], 211 | magenta: [255,0,255], 212 | maroon: [128,0,0], 213 | mediumaquamarine: [102,205,170], 214 | mediumblue: [0,0,205], 215 | mediumorchid: [186,85,211], 216 | mediumpurple: [147,112,219], 217 | mediumseagreen: [60,179,113], 218 | mediumslateblue: [123,104,238], 219 | mediumspringgreen: [0,250,154], 220 | mediumturquoise: [72,209,204], 221 | mediumvioletred: [199,21,133], 222 | midnightblue: [25,25,112], 223 | mintcream: [245,255,250], 224 | mistyrose: [255,228,225], 225 | moccasin: [255,228,181], 226 | navajowhite: [255,222,173], 227 | navy: [0,0,128], 228 | oldlace: [253,245,230], 229 | olive: [128,128,0], 230 | olivedrab: [107,142,35], 231 | orange: [255,165,0], 232 | orangered: [255,69,0], 233 | orchid: [218,112,214], 234 | palegoldenrod: [238,232,170], 235 | palegreen: [152,251,152], 236 | paleturquoise: [175,238,238], 237 | palevioletred: [219,112,147], 238 | papayawhip: [255,239,213], 239 | peachpuff: [255,218,185], 240 | peru: [205,133,63], 241 | pink: [255,192,203], 242 | plum: [221,160,221], 243 | powderblue: [176,224,230], 244 | purple: [128,0,128], 245 | rebeccapurple: [102,51,153], 246 | red: [255,0,0], 247 | rosybrown: [188,143,143], 248 | royalblue: [65,105,225], 249 | saddlebrown: [139,69,19], 250 | salmon: [250,128,114], 251 | sandybrown: [244,164,96], 252 | seagreen: [46,139,87], 253 | seashell: [255,245,238], 254 | sienna: [160,82,45], 255 | silver: [192,192,192], 256 | skyblue: [135,206,235], 257 | slateblue: [106,90,205], 258 | slategray: [112,128,144], 259 | slategrey: [112,128,144], 260 | snow: [255,250,250], 261 | springgreen: [0,255,127], 262 | steelblue: [70,130,180], 263 | tan: [210,180,140], 264 | teal: [0,128,128], 265 | thistle: [216,191,216], 266 | tomato: [255,99,71], 267 | turquoise: [64,224,208], 268 | violet: [238,130,238], 269 | wheat: [245,222,179], 270 | white: [255,255,255], 271 | whitesmoke: [245,245,245], 272 | yellow: [255,255,0], 273 | yellowgreen: [154,205,50] 274 | }, 275 | 276 | Color: { 277 | isColorObject: true, 278 | 279 | create: function(r, g, b, a) { 280 | var obj = Object.create(this); 281 | obj._init(r, g, b, a); 282 | return obj; 283 | }, 284 | 285 | _init: function(r, g, b, a) { 286 | this.red = r; 287 | this.green = g; 288 | this.blue = b; 289 | this.alpha = a; 290 | }, 291 | 292 | toString: function () { 293 | return "rgba(" + Math.floor(this.red) + "," + Math.floor(this.green) + "," + Math.floor(this.blue) + "," + this.alpha + ")"; 294 | } 295 | } 296 | 297 | }; -------------------------------------------------------------------------------- /src/bitlib_context.js: -------------------------------------------------------------------------------- 1 | bitlib.context = function (w, h, parent) { 2 | if(w === 0 || h === 0) { 3 | w = window.innerWidth; 4 | h = window.innerHeight; 5 | } 6 | var canvas = document.createElement("canvas"); 7 | var context = canvas.getContext("2d"); 8 | bitlib.extendContext(context); 9 | canvas.style.display = "block"; 10 | context.setSize(w || 600, h || 600); 11 | document.body.style.margin = "0"; 12 | document.body.style.padding = "0"; 13 | parent = parent || document.body; 14 | parent.appendChild(canvas); 15 | return context; 16 | }; 17 | 18 | bitlib.extendContext = function(context) { 19 | 20 | context.setShadow =function(color, offsetX, offsetY, blur) { 21 | if(typeof color === "number") { 22 | this.shadowColor = bitlib.color.rgba(0,0,0, color); 23 | } 24 | else { 25 | this.shadowColor = color; 26 | } 27 | this.shadowOffsetX = offsetX; 28 | this.shadowOffsetY = offsetY; 29 | this.shadowBlur = blur; 30 | }; 31 | 32 | context.clear = function(color) { 33 | this.save(); 34 | this.setTransform(1, 0, 0, 1, 0, 0); 35 | if(color) { 36 | this.fillStyle = color; 37 | this.fillRect(0, 0, this.canvas.width, this.canvas.height); 38 | } 39 | else { 40 | this.clearRect(0, 0, this.canvas.width, this.canvas.height); 41 | } 42 | this.restore(); 43 | } 44 | 45 | context.circle = function(x, y, radius) { 46 | this.arc(x, y, radius, 0, Math.PI * 2); 47 | } 48 | 49 | context.fillCircle = function(x, y, radius) { 50 | this.beginPath(); 51 | this.circle(x, y, radius); 52 | this.fill(); 53 | } 54 | 55 | context.strokeCircle = function(x, y, radius) { 56 | this.beginPath(); 57 | this.circle(x, y, radius); 58 | this.stroke(); 59 | } 60 | 61 | context.ellipse = function(x, y, xr, yr) { 62 | this.save(); 63 | this.translate(x, y); 64 | this.scale(xr, yr); 65 | this.arc(0, 0, 1, 0, Math.PI * 2); 66 | this.restore(); 67 | } 68 | 69 | context.setSize = function(w, h) { 70 | this.width = this.canvas.width = w; 71 | this.height = this.canvas.height = h; 72 | }; 73 | 74 | context.multiCurve = function(points) { 75 | this.moveTo(points[0].x, points[0].y); 76 | this.lineTo((points[0].x + points[1].x) / 2, (points[0].y + points[1].y) / 2); 77 | for(var i = 1; i < points.length - 1; i++) { 78 | var p0 = points[i], 79 | p1 = points[i + 1], 80 | midx = (p0.x + p1.x) / 2, 81 | midy = (p0.y + p1.y) / 2; 82 | this.quadraticCurveTo(p0.x, p0.y, midx, midy); 83 | 84 | } 85 | var p = points[points.length - 1]; 86 | this.lineTo(p.x, p.y); 87 | }; 88 | 89 | context.strokeMultiCurve = function(points) { 90 | this.beginPath(); 91 | this.multiCurve(points); 92 | this.stroke(); 93 | }; 94 | 95 | context.multiLoop = function(points) { 96 | var pA = points[0], 97 | pB = points[1], 98 | pZ = points[points.length - 1], 99 | mid1x = (pZ.x + pA.x) / 2, 100 | mid1y = (pZ.y + pA.y) / 2; 101 | this.moveTo(mid1x, mid1y); 102 | for(var i = 0; i < points.length - 1; i++) { 103 | var p0 = points[i], 104 | p1 = points[i + 1], 105 | midx = (p0.x + p1.x) / 2, 106 | midy = (p0.y + p1.y) / 2; 107 | this.quadraticCurveTo(p0.x, p0.y, midx, midy); 108 | } 109 | this.quadraticCurveTo(pZ.x, pZ.y, mid1x, mid1y); 110 | }; 111 | 112 | context.strokeMultiLoop = function(points) { 113 | this.beginPath(); 114 | this.multiLoop(points); 115 | this.stroke(); 116 | }; 117 | 118 | context.fillMultiLoop = function(points) { 119 | this.beginPath(); 120 | this.multiLoop(points); 121 | this.fill(); 122 | }; 123 | 124 | context.path = function(points) { 125 | context.moveTo(points[0].x, points[0].y); 126 | for(var i = 1; i < points.length; i++) { 127 | context.lineTo(points[i].x, points[i].y); 128 | } 129 | }; 130 | 131 | context.strokePath = function(points, close) { 132 | context.beginPath(); 133 | context.path(points); 134 | if(close) { 135 | context.closePath(); 136 | } 137 | context.stroke(); 138 | }; 139 | 140 | context.fillPath = function(points) { 141 | context.beginPath(); 142 | context.path(points); 143 | context.fill(); 144 | }; 145 | 146 | context.line = function(x0, y0, x1, y1) { 147 | context.beginPath(); 148 | context.moveTo(x0, y0); 149 | context.lineTo(x1, y1); 150 | context.stroke(); 151 | }; 152 | 153 | 154 | 155 | 156 | 157 | }; -------------------------------------------------------------------------------- /src/bitlib_image.js: -------------------------------------------------------------------------------- 1 | bitlib.image = function(url, callback) { 2 | var img = document.createElement("img"); 3 | img.addEventListener("load", function() { 4 | callback(img); 5 | }); 6 | img.src = url; 7 | return img; 8 | } 9 | 10 | bitlib.imageData = function(url, callback) { 11 | var image = bitlib.image(url, function() { 12 | var w = image.width, 13 | h = image.height, 14 | canvas = document.createElement("canvas"), 15 | context = canvas.getContext("2d"); 16 | canvas.width = w; 17 | canvas.height = h; 18 | context.drawImage(image, 0, 0); 19 | try { 20 | callback(context.getImageData(0, 0, w, h)); 21 | } 22 | catch(e) { 23 | console.log(e.message); 24 | callback(null); 25 | } 26 | }); 27 | } -------------------------------------------------------------------------------- /src/bitlib_math.js: -------------------------------------------------------------------------------- 1 | bitlib.math = { 2 | norm: function (value, min, max) { 3 | return (value - min) / (max - min); 4 | }, 5 | 6 | lerp: function (min, max, t) { 7 | return min + (max - min) * t; 8 | }, 9 | 10 | map: function (srcValue, srcMin, srcMax, dstMin, dstMax) { 11 | var norm = this.norm(srcValue, srcMin, srcMax); 12 | return this.lerp(dstMin, dstMax, norm); 13 | }, 14 | 15 | clamp: function (value, min, max) { 16 | return Math.min(Math.max(value, min), max); 17 | }, 18 | 19 | dotProduct: function(x0, y0, x1, y1, x2, y2, x3, y3) { 20 | var dx0 = x1 - x0, 21 | dy0 = y1 - y0, 22 | dx1 = x3 - x2, 23 | dy1 = y3 - y2; 24 | return dx0 * dx1 + dy0 * dy1; 25 | }, 26 | 27 | angleBetween: function(x0, y0, x1, y1, x2, y2, x3, y3) { 28 | var dp = this.dotProduct(x0, y0, x1, y1, x2, y2, x3, y3), 29 | mag0 = this.dist(x0, y0, x1, y1), 30 | mag1 = this.dist(x2, y2, x3, y3); 31 | return Math.acos(dp / mag0 / mag1); 32 | }, 33 | 34 | polarToPoint: function (angle, radius) { 35 | return { 36 | x: Math.cos(angle) * radius, 37 | y: Math.sin(angle) * radius 38 | }; 39 | }, 40 | 41 | pointToPolar: function(p) { 42 | return { 43 | angle: Math.atan2(p.y, p.x), 44 | radius: this.magnitude(p) 45 | }; 46 | }, 47 | 48 | magnitude: function(p) { 49 | return this.dist(0, 0, p.x, p.y); 50 | }, 51 | 52 | dist: function (x0, y0, x1, y1) { 53 | if(arguments.length === 2) { 54 | return this.dist(x0.x, x0.y, y0.x, y0.y); 55 | } 56 | var dx = x1 - x0, 57 | dy = y1 - y0; 58 | return Math.sqrt(dx * dx + dy * dy); 59 | }, 60 | 61 | lerpPoint: function(p0, p1, t) { 62 | return { 63 | x: this.lerp(p0.x, p1.x, t), 64 | y: this.lerp(p0.y, p1.y, t) 65 | }; 66 | }, 67 | 68 | bezier: function(p0, p1, p2, p3, t) { 69 | var oneMinusT = 1 - t, 70 | m0 = oneMinusT * oneMinusT * oneMinusT, 71 | m1 = 3 * oneMinusT * oneMinusT * t, 72 | m2 = 3 * oneMinusT * t * t, 73 | m3 = t * t * t; 74 | return { 75 | x: m0 * p0.x + m1 * p1.x + m2 * p2.x + m3 * p3.x, 76 | y: m0 * p0.y + m1 * p1.y + m2 * p2.y + m3 * p3.y 77 | }; 78 | }, 79 | 80 | quadratic: function(p0, p1, p2, t) { 81 | var oneMinusT = 1 - t, 82 | m0 = oneMinusT * oneMinusT, 83 | m1 = 2 * oneMinusT * t, 84 | m2 = t * t; 85 | return { 86 | x: m0 * p0.x + m1 * p1.x + m2 * p2.x, 87 | y: m0 * p0.y + m1 * p1.y + m2 * p2.y 88 | } 89 | 90 | }, 91 | 92 | pointInCircle: function(px, py, cx, cy, cr) { 93 | var dist = this.dist(px, py, cx, cy); 94 | return dist <= cr; 95 | }, 96 | 97 | pointInRect: function(px, py, rx, ry, rw, rh) { 98 | return px >= rx && 99 | py >= ry && 100 | px <= rx + rw && 101 | py <= ry + rh; 102 | }, 103 | 104 | segmentIntersect: function(p0, p1, p2, p3) { 105 | var A1 = p1.y - p0.y, 106 | B1 = p0.x - p1.x, 107 | C1 = A1 * p0.x + B1 * p0.y, 108 | A2 = p3.y - p2.y, 109 | B2 = p2.x - p3.x, 110 | C2 = A2 * p2.x + B2 * p2.y, 111 | denominator = A1 * B2 - A2 * B1; 112 | 113 | if (denominator == 0) { 114 | return null; 115 | } 116 | 117 | var intersectX = (B2 * C1 - B1 * C2) / denominator, 118 | intersectY = (A1 * C2 - A2 * C1) / denominator, 119 | rx0 = (intersectX - p0.x) / (p1.x - p0.x), 120 | ry0 = (intersectY - p0.y) / (p1.y - p0.y), 121 | rx1 = (intersectX - p2.x) / (p3.x - p2.x), 122 | ry1 = (intersectY - p2.y) / (p3.y - p2.y); 123 | 124 | if (((rx0 >= 0 && rx0 <= 1) || (ry0 >= 0 && ry0 <= 1)) && 125 | ((rx1 >= 0 && rx1 <= 1) || (ry1 >= 0 && ry1 <= 1))) { 126 | return { 127 | x: intersectX, 128 | y: intersectY 129 | }; 130 | } 131 | else { 132 | return null; 133 | } 134 | }, 135 | 136 | tangentPointToCircle: function(x, y, cx, cy, cr, anticlockwise) { 137 | var dist = bitlib.math.dist(x, y, cx, cy), 138 | dir = anticlockwise ? 1 : -1, 139 | angle = Math.acos(-cr / dist) * dir, 140 | baseAngle = Math.atan2(cy - y, cx - x), 141 | totalAngle = baseAngle + angle; 142 | 143 | return { 144 | x: cx + Math.cos(totalAngle) * cr, 145 | y: cy + Math.sin(totalAngle) * cr 146 | }; 147 | } 148 | }; 149 | -------------------------------------------------------------------------------- /src/bitlib_random.js: -------------------------------------------------------------------------------- 1 | bitlib.random = { 2 | _seed: Date.now(), 3 | _a: 1664525, 4 | _c: 1013904223, 5 | _m: Math.pow(2, 32), 6 | 7 | seed: function(seed) { 8 | this._seed = seed; 9 | }, 10 | 11 | _int: function() { 12 | // range [0, 2^32) 13 | this._seed = (this._seed * this._a + this._c) % this._m; 14 | return this._seed; 15 | }, 16 | 17 | _float: function() { 18 | // range [0, 1) 19 | return this._int() / this._m; 20 | }, 21 | 22 | bool: function(percent) { 23 | // percent is chance of getting true 24 | if(percent == null) { 25 | percent = 0.5; 26 | } 27 | return this._float() < percent; 28 | }, 29 | 30 | float: function(min, max) { 31 | // range [min, max) 32 | if(arguments.length === 1) { 33 | return this._float() * min; 34 | } 35 | if(arguments.length === 2) { 36 | return min + this._float() * (max - min); 37 | } 38 | return this._float(); 39 | }, 40 | 41 | int: function(min, max) { 42 | // range [min, max) 43 | if(arguments.length === 1) { 44 | return Math.floor(this._float() * min); 45 | } 46 | if(arguments.length === 2) { 47 | return Math.floor(this.float(min, max)); 48 | } 49 | return this._int(); 50 | }, 51 | 52 | power: function(min, max, power) { 53 | if(arguments.length === 2) { 54 | power = max; 55 | max = min; 56 | min = 0; 57 | } 58 | return min + Math.pow(this.float(1), power) * (max - min); 59 | }, 60 | 61 | powerInt: function(min, max, power) { 62 | return Math.floor(this.power(min, max, power)); 63 | }, 64 | 65 | gauss: function(min, max, g) { 66 | if(arguments.length === 2) { 67 | g = max; 68 | max = min; 69 | min = 0; 70 | } 71 | var total = 0; 72 | for(var i = 0; i < g; i++) { 73 | total += this.float(min, max); 74 | } 75 | return total / g; 76 | }, 77 | 78 | chooser: function() { 79 | 80 | return { 81 | choices: [], 82 | total: 0, 83 | 84 | addChoice: function (choice, weight) { 85 | if (weight == null) weight = 1; 86 | 87 | this.choices.push({ 88 | weight: weight, 89 | choice: choice 90 | }); 91 | this.total += weight; 92 | return this; 93 | }, 94 | 95 | getChoice: function () { 96 | var rand = bitlib.random.float(0, this.total); 97 | for (var i = 0; i < this.choices.length; i++) { 98 | var choice = this.choices[i]; 99 | if (rand < choice.weight) { 100 | return choice.choice; 101 | } 102 | rand -= choice.weight; 103 | } 104 | } 105 | } 106 | } 107 | }; 108 | -------------------------------------------------------------------------------- /src/chooser.js: -------------------------------------------------------------------------------- 1 | bitlib.chooser = function() { 2 | 3 | return { 4 | choices: [], 5 | total: 0, 6 | 7 | addChoice: function(weight, option) { 8 | this.choices.push({ 9 | weight: weight, 10 | option: option 11 | }); 12 | this.total += weight; 13 | return this; 14 | }, 15 | 16 | getChoice: function() { 17 | var rand = bitlib.random.float(0, this.total); 18 | for(var i = 0; i < this.choices.length; i++) { 19 | var choice = this.choices[i]; 20 | if(rand < choice.weight) { 21 | return choice.option; 22 | } 23 | rand -= choice.weight; 24 | } 25 | } 26 | } 27 | }; -------------------------------------------------------------------------------- /test/boyhowdy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit101/bitlibjs/98f96936f25d15ff0c2c140d4ed2509f84047166/test/boyhowdy.jpg -------------------------------------------------------------------------------- /test/testPath.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | test image 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/testPower.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/testTangent.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/testanim.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/testanim.js: -------------------------------------------------------------------------------- 1 | var anim1 = bitlib.anim(onRender1), 2 | context1 = bitlib.context(100, 100), 3 | angle = 0; 4 | 5 | draw1(); 6 | 7 | document.addEventListener("click", function() { 8 | anim1.toggle(); 9 | }); 10 | 11 | function onRender1() { 12 | draw1(); 13 | angle += 0.1; 14 | } 15 | 16 | function draw1() { 17 | context1.clearRect(0, 0, 100, 100); 18 | context1.beginPath(); 19 | context1.moveTo(50, 50); 20 | context1.lineTo(50 + Math.cos(angle) * 50, 50 + Math.sin(angle) * 50); 21 | context1.stroke(); 22 | 23 | context1.beginPath(); 24 | context1.arc(50, 50, 49, 0, Math.PI * 2); 25 | context1.stroke(); 26 | } 27 | 28 | var anim2 = bitlib.anim(onRender2, 10), 29 | context2 = bitlib.context(200, 100), 30 | x = 0; 31 | 32 | document.addEventListener("keyup", function() { 33 | anim2.toggle(); 34 | }); 35 | 36 | onRender2(); 37 | 38 | function onRender2() { 39 | context2.clearRect(0, 0, 200, 100); 40 | context2.beginPath(); 41 | context2.moveTo(x, 0); 42 | context2.lineTo(x, 100); 43 | context2.stroke(); 44 | 45 | x++; 46 | if(x > 200) { 47 | x = 0; 48 | } 49 | } -------------------------------------------------------------------------------- /test/testbezier.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/testbezier.js: -------------------------------------------------------------------------------- 1 | var w = 600, 2 | h = 600, 3 | context = bitlib.context(w, h); 4 | context.lineWidth = 0.5; 5 | 6 | var pointsA = [ 7 | { 8 | x: 0, 9 | y: 0 10 | }, 11 | p1 = { 12 | x: 600, 13 | y: 0 14 | }, 15 | p2 = { 16 | x: 0, 17 | y: 600 18 | }, 19 | p3 = { 20 | x: 600, 21 | y: 600 22 | } 23 | ], 24 | pointsB = [ 25 | { 26 | x: 200, 27 | y: 600 28 | }, 29 | p1 = { 30 | x: 600, 31 | y: 600 32 | }, 33 | p2 = { 34 | x: 600, 35 | y: 600 36 | }, 37 | p3 = { 38 | x: 0, 39 | y: 600 40 | } 41 | ], 42 | anim = bitlib.anim(draw).start(), 43 | angle = 0; 44 | 45 | 46 | function draw() { 47 | var tt = Math.sin(angle) * 0.5 + 0.5; 48 | var p0 = bitlib.math.lerpPoint(pointsA[0], pointsB[0], tt), 49 | p1 = bitlib.math.lerpPoint(pointsA[1], pointsB[1], tt), 50 | p2 = bitlib.math.lerpPoint(pointsA[2], pointsB[2], tt), 51 | p3 = bitlib.math.lerpPoint(pointsA[3], pointsB[3], tt); 52 | context.clearRect(0, 0, w, h); 53 | context.beginPath(); 54 | context.moveTo(p0.x, p0.y); 55 | context.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); 56 | context.stroke(); 57 | 58 | context.beginPath(); 59 | for (var t = 0; t <= 1; t += 0.01) { 60 | var p = bitlib.math.bezier(p0, p1, p2, p3, t); 61 | context.moveTo(600, 0); 62 | context.lineTo(p.x, p.y); 63 | } 64 | context.stroke(); 65 | angle += 0.01; 66 | } 67 | 68 | function draw2() { 69 | p0.x = 0; 70 | p0.y = 600; 71 | p1.x = 600; 72 | p1.y = 300; 73 | p2.x = 0; 74 | p2.y = 0; 75 | 76 | context.beginPath(); 77 | context.moveTo(p0.x, p0.y); 78 | context.quadraticCurveTo(p1.x, p1.y, p2.x, p2.y); 79 | context.stroke(); 80 | 81 | context.beginPath(); 82 | for (var t = 0; t <= 1; t += 0.01) { 83 | var p = bitlib.math.quadratic(p0, p1, p2, t); 84 | context.moveTo(0, 300); 85 | context.lineTo(p.x, p.y); 86 | } 87 | context.stroke(); 88 | } -------------------------------------------------------------------------------- /test/testcolors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/testcolors.js: -------------------------------------------------------------------------------- 1 | var w = 340, 2 | h = 350, 3 | context = bitlib.context(w, h); 4 | 5 | testRGB(0, 10); 6 | testRGBA(0, 50); 7 | testNumber(0, 90); 8 | testRandomRGB(120, 10); 9 | testRandomRGBSeeded(240, 10); 10 | testGray(0, 130); 11 | testRandomGray(120, 130); 12 | testRandomGraySeeded(240, 130); 13 | testStrings(0, 250); 14 | testHSV(120, 250); 15 | testLerp(240, 250); 16 | 17 | 18 | function testRGB(x, y) { 19 | context.save(); 20 | context.translate(x, y); 21 | context.fillStyle = bitlib.color.rgb(255, 0, 0); 22 | context.fillRect(0, 0, 19, 19); 23 | context.fillStyle = bitlib.color.rgb(255, 255, 0); 24 | context.fillRect(20, 0, 19, 19); 25 | context.fillStyle = bitlib.color.rgb(0, 255, 0); 26 | context.fillRect(40, 0, 19, 19); 27 | context.fillStyle = bitlib.color.rgb(0, 255, 255); 28 | context.fillRect(60, 0, 19, 19); 29 | context.fillStyle = bitlib.color.rgb(0, 0, 255); 30 | context.fillRect(80, 0, 19, 19); 31 | context.fillStyle = bitlib.color.rgb(0, 0, 0); 32 | context.fillText("RGB", 0, -1); 33 | context.restore(); 34 | } 35 | 36 | function testRGBA(x, y) { 37 | context.save(); 38 | context.translate(x, y); 39 | context.fillStyle = bitlib.color.rgba(255, 0, 0, .2); 40 | context.fillRect(0, 0, 19, 19); 41 | context.fillStyle = bitlib.color.rgba(255, 255, 0, .4); 42 | context.fillRect(20, 0, 19, 19); 43 | context.fillStyle = bitlib.color.rgba(0, 255, 0, .6); 44 | context.fillRect(40, 0, 19, 19); 45 | context.fillStyle = bitlib.color.rgba(0, 255, 255, 8); 46 | context.fillRect(60, 0, 19, 19); 47 | context.fillStyle = bitlib.color.rgba(0, 0, 255, 1); 48 | context.fillRect(80, 0, 19, 19); 49 | context.fillStyle = bitlib.color.rgba(0, 0, 0, 1); 50 | context.fillText("RGBA", 0, -1); 51 | context.restore(); 52 | } 53 | 54 | function testNumber(x, y) { 55 | context.save(); 56 | context.translate(x, y); 57 | context.fillStyle = bitlib.color.number(0xff0000); 58 | context.fillRect(0, 0, 19, 19); 59 | context.fillStyle = bitlib.color.number(0xffff00); 60 | context.fillRect(20, 0, 19, 19); 61 | context.fillStyle = bitlib.color.number(0x00ff00); 62 | context.fillRect(40, 0, 19, 19); 63 | context.fillStyle = bitlib.color.number(0x00ffff); 64 | context.fillRect(60, 0, 19, 19); 65 | context.fillStyle = bitlib.color.number(0x0000ff); 66 | context.fillRect(80, 0, 19, 19); 67 | context.fillStyle = bitlib.color.number(0x000000); 68 | context.fillText("Number", 0, -1); 69 | context.restore(); 70 | } 71 | 72 | function testRandomRGB(x, y) { 73 | bitlib.random.seed(0); 74 | context.save(); 75 | context.translate(x, y); 76 | for(var y = 0; y < 100; y += 20) { 77 | for (var x = 0; x < 100; x += 20) { 78 | context.fillStyle = bitlib.color.randomRGB(); 79 | context.fillRect(x, y, 19, 19); 80 | } 81 | } 82 | context.fillStyle = "black"; 83 | context.fillText("randomRGB", 0, -1); 84 | context.restore(); 85 | } 86 | 87 | function testRandomRGBSeeded(x, y) { 88 | context.save(); 89 | context.translate(x, y); 90 | for(var y = 0; y < 100; y += 20) { 91 | bitlib.random.seed(0); 92 | for (var x = 0; x < 100; x += 20) { 93 | context.fillStyle = bitlib.color.randomRGB(); 94 | context.fillRect(x, y, 19, 19); 95 | } 96 | } 97 | context.fillStyle = "black"; 98 | context.fillText("randomRGB seeded", 0, -1); 99 | context.restore(); 100 | } 101 | 102 | function testGray(x, y) { 103 | context.save(); 104 | context.translate(x, y); 105 | context.fillStyle = bitlib.color.gray(0); 106 | context.fillRect(0, 0, 19, 19); 107 | context.fillStyle = bitlib.color.gray(51); 108 | context.fillRect(20, 0, 19, 19); 109 | context.fillStyle = bitlib.color.gray(102); 110 | context.fillRect(40, 0, 19, 19); 111 | context.fillStyle = bitlib.color.gray(153); 112 | context.fillRect(60, 0, 19, 19); 113 | context.fillStyle = bitlib.color.gray(204); 114 | context.fillRect(80, 0, 19, 19); 115 | context.fillStyle = bitlib.color.gray(0); 116 | context.fillText("Gray", 0, -1); 117 | context.restore(); 118 | } 119 | 120 | function testRandomGray(x, y) { 121 | bitlib.random.seed(0); 122 | context.save(); 123 | context.translate(x, y); 124 | for(var y = 0; y < 100; y += 20) { 125 | for (var x = 0; x < 100; x += 20) { 126 | context.fillStyle = bitlib.color.randomGray(); 127 | context.fillRect(x, y, 19, 19); 128 | } 129 | } 130 | context.fillStyle = "black"; 131 | context.fillText("randomGray", 0, -1); 132 | context.restore(); 133 | } 134 | 135 | function testRandomGraySeeded(x, y) { 136 | context.save(); 137 | context.translate(x, y); 138 | for(var y = 0; y < 100; y += 20) { 139 | bitlib.random.seed(0); 140 | for (var x = 0; x < 100; x += 20) { 141 | context.fillStyle = bitlib.color.randomGray(); 142 | context.fillRect(x, y, 19, 19); 143 | } 144 | } 145 | context.fillStyle = "black"; 146 | context.fillText("randomGray seeded", 0, -1); 147 | context.restore(); 148 | } 149 | 150 | function testStrings(x, y) { 151 | var strings = [ 152 | "#ff0000", 153 | "#ffff00", 154 | "#00ff00", 155 | "#00ffff", 156 | "#0000ff", 157 | 158 | "#f00", 159 | "#ff0", 160 | "#0f0", 161 | "#0ff", 162 | "#00f", 163 | 164 | "rgb(255, 0, 0)", 165 | "rgb(255, 255, 0)", 166 | "rgb(0, 255, 0)", 167 | "rgb(0, 255, 255)", 168 | "rgb(0, 0, 255)", 169 | 170 | "rgba(255, 0, 0, 0.2)", 171 | "rgba(255, 255, 0, 0.4)", 172 | "rgba(0, 255, 0, 0.6)", 173 | "rgba(0, 255, 2550, 8)", 174 | "rgba(0, 0, 255, 1)", 175 | 176 | "red", 177 | "yellow", 178 | "green", 179 | "cyan", 180 | "blue" 181 | ]; 182 | var index = 0; 183 | context.save(); 184 | context.translate(x, y); 185 | for(var y = 0; y < 100; y += 20) { 186 | bitlib.random.seed(0); 187 | for (var x = 0; x < 100; x += 20) { 188 | context.fillStyle = bitlib.color.string(strings[index++]); 189 | context.fillRect(x, y, 19, 19); 190 | } 191 | } 192 | context.fillStyle = "black"; 193 | context.fillText("strings", 0, -1); 194 | context.restore(); 195 | 196 | } 197 | 198 | function testHSV(x, y) { 199 | context.save(); 200 | context.translate(x, y); 201 | for (var x = 0; x < 100; x += 20) { 202 | context.fillStyle = bitlib.color.hsv(x / 100 * 360, 1, 1); 203 | context.fillRect(x, 0, 19, 19); 204 | } 205 | for (var x = 0; x < 100; x += 20) { 206 | context.fillStyle = bitlib.color.hsv(x / 100 * 360, x / 100, 1); 207 | context.fillRect(x, 20, 19, 19); 208 | } 209 | for (var x = 0; x < 100; x += 20) { 210 | context.fillStyle = bitlib.color.hsv(x / 100 * 360, 1, x / 100); 211 | context.fillRect(x, 40, 19, 19); 212 | } 213 | context.fillStyle = "black"; 214 | context.fillText("hsv", 0, -1); 215 | context.restore(); 216 | } 217 | 218 | function testLerp(x, y) { 219 | context.save(); 220 | context.translate(x, y); 221 | 222 | var a = "#ff0000", 223 | b = "rgb(0, 0, 255)"; 224 | for(var x = 0; x < 100; x += 20) { 225 | context.fillStyle = bitlib.color._lerp(x / 100, a, b); 226 | context.fillRect(x, 0, 19, 19); 227 | } 228 | 229 | b = "rgba(0, 0, 255, 0)"; 230 | for(var x = 0; x < 100; x += 20) { 231 | context.fillStyle = bitlib.color._lerp(x / 100, a, b); 232 | context.fillRect(x, 20, 19, 19); 233 | } 234 | 235 | b = "blue"; 236 | for(var x = 0; x < 100; x += 20) { 237 | context.fillStyle = bitlib.color._lerp(x / 100, a, b); 238 | context.fillRect(x, 40, 19, 19); 239 | } 240 | 241 | b = 0x0000ff; 242 | for(var x = 0; x < 100; x += 20) { 243 | context.fillStyle = bitlib.color._lerp(x / 100, a, b); 244 | context.fillRect(x, 60, 19, 19); 245 | } 246 | 247 | b = "#00f"; 248 | for(var x = 0; x < 100; x += 20) { 249 | context.fillStyle = bitlib.color._lerp(x / 100, a, b); 250 | context.fillRect(x, 80, 19, 19); 251 | } 252 | context.fillStyle = "black"; 253 | context.fillText("_lerp", 0, -1); 254 | context.restore(); 255 | } -------------------------------------------------------------------------------- /test/testgauss.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/testgauss.js: -------------------------------------------------------------------------------- 1 | var context = bitlib.context(0, 0), 2 | width = context.width, 3 | height = context.height; 4 | 5 | // bitlib.random.seed(0); 6 | 7 | for(var i = 0; i < 10000; i++) { 8 | var x = bitlib.random.gauss(width, 100), 9 | y = bitlib.random.gauss(height, 100); 10 | context.fillRect(x, y, 1, 1); 11 | } -------------------------------------------------------------------------------- /test/testimage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | test image 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/testimage.js: -------------------------------------------------------------------------------- 1 | var context = bitlib.context(); 2 | 3 | var url = "boyhowdy.jpg"; 4 | // var url = "https://www.royalcanin.com/~/media/Royal-Canin/Product-Categories/cat-adult-landing-hero.ashx" 5 | var image = bitlib.image(url, function(img) { 6 | // note: image is the same as img in this case. use either 7 | document.body.appendChild(img); 8 | }); 9 | 10 | 11 | bitlib.imageData(url, function(imageData) { 12 | if(imageData) { 13 | context.canvas.width = imageData.width; 14 | context.canvas.height = imageData.height; 15 | context.putImageData(imageData, 0, 0); 16 | } 17 | context.font = "60px Arial"; 18 | context.fillStyle = "white"; 19 | context.fillText("CANVAS", 50, 150); 20 | context.strokeText("CANVAS", 50, 150); 21 | }) -------------------------------------------------------------------------------- /test/testmulticurve.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/testmulticurve.js: -------------------------------------------------------------------------------- 1 | var context = bitlib.context(0, 0), 2 | width = context.width, 3 | height = context.height; 4 | 5 | var points = []; 6 | 7 | for(var i = 0; i < 10; i++) { 8 | points.push({ 9 | x: bitlib.random.int(width), 10 | y: bitlib.random.int(height) 11 | }); 12 | } 13 | 14 | 15 | for(var i = 0; i < points.length; i++) { 16 | var p = points[i]; 17 | context.fillCircle(p.x, p.y, 2); 18 | context.fillText(i, p.x + 5, p.y); 19 | } 20 | context.setShadow("rgba(0,0,0,0.5", 5, 5, 10); 21 | 22 | context.lineWidth = 10; 23 | context.strokeStyle = bitlib.color.randomRGB(); 24 | context.strokeMultiCurve(points); 25 | 26 | for(var i = 0; i < 4; i++) { 27 | context.lineWidth = 6 - i * 2; 28 | context.strokeStyle = "rgba(255,255,255,0.1)"; 29 | context.save(); 30 | context.translate(-2, -2); 31 | context.strokeMultiCurve(points); 32 | context.restore(); 33 | } -------------------------------------------------------------------------------- /test/testpath.js: -------------------------------------------------------------------------------- 1 | var context = bitlib.context(0, 0), 2 | width = context.width, 3 | height = context.height; 4 | 5 | 6 | var points = []; 7 | for(var x = 0; x < width; x += 20) { 8 | var y = bitlib.random.float(100); 9 | points.push({ 10 | x: x, 11 | y: y 12 | }); 13 | } 14 | 15 | context.strokePath(points); 16 | 17 | points = []; 18 | 19 | var num = 12; 20 | for(var i = 0; i < num; i++) { 21 | var a = i / num * Math.PI * 2; 22 | points.push({ 23 | x: Math.cos(a) * bitlib.random.float(50, 250), 24 | y: Math.sin(a) * bitlib.random.float(50, 250) 25 | }) 26 | } 27 | 28 | context.translate(260, height / 2); 29 | context.strokePath(points); 30 | 31 | context.translate(500, 0); 32 | context.strokePath(points, true); 33 | 34 | context.translate(500, 0); 35 | context.fillPath(points); 36 | 37 | -------------------------------------------------------------------------------- /test/testpower.js: -------------------------------------------------------------------------------- 1 | var context = bitlib.context(0, 0), 2 | width = context.width, 3 | height = context.height; 4 | context.strokeStyle = "red"; 5 | context.translate(0.5, 0); 6 | var vals = []; 7 | 8 | var power = 50; 9 | 10 | var max = 0; 11 | for(var i = 0; i < 10000; i++) { 12 | update(); 13 | } 14 | 15 | var scale = height / 2 / max; 16 | 17 | context.beginPath(); 18 | for(var i = 0; i < vals.length; i++) { 19 | var val = vals[i]; 20 | context.moveTo(i, height); 21 | context.lineTo(i, height - val * scale); 22 | } 23 | context.stroke(); 24 | 25 | 26 | function update() { 27 | var val = bitlib.random.powerInt(0, width, power); 28 | vals[val] = (vals[val] || 0) + 1; 29 | max = Math.max(vals[val], max); 30 | context.fillCircle(val, bitlib.random.float(height / 2), 2); 31 | } 32 | 33 | -------------------------------------------------------------------------------- /test/testrandom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/testrandom.js: -------------------------------------------------------------------------------- 1 | var chooser = bitlib.random.chooser() 2 | .addChoice("one", 1) 3 | .addChoice("three", 3) 4 | .addChoice("six", 6) 5 | .addChoice("nine", 9); 6 | 7 | 8 | var results = { 9 | one: 0, 10 | three: 0, 11 | six: 0, 12 | nine: 0 13 | }; 14 | 15 | 16 | for(var i = 0; i < 1000; i++) { 17 | var choice = chooser.getChoice(); 18 | results[choice]++ 19 | } 20 | 21 | console.log(results); 22 | -------------------------------------------------------------------------------- /test/testshadow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/testshadow.js: -------------------------------------------------------------------------------- 1 | var context = bitlib.context(0, 0); 2 | 3 | context.setShadow("rgba(255, 0, 0, 0.5)", 10, 10, 10); 4 | 5 | context.fillRect(50, 50, 100, 100); 6 | 7 | for(var i = 0; i < 5; i++) { 8 | context.setShadow(i / 5, 10, 10, 10); 9 | 10 | context.fillRect(250, 50 + i * 150, 100, 100); 11 | } -------------------------------------------------------------------------------- /test/testtangent.js: -------------------------------------------------------------------------------- 1 | var context = bitlib.context(0, 0), 2 | width = context.width, 3 | height = context.height; 4 | 5 | var c = { 6 | x: width / 2, 7 | y: height / 2, 8 | r: 100 9 | }; 10 | 11 | document.body.addEventListener("mousemove", function(event) { 12 | var x = event.clientX, 13 | y = event.clientY, 14 | tan0 = bitlib.math.tangentPointToCircle(x, y, c.x, c.y, c.r), 15 | tan1 = bitlib.math.tangentPointToCircle(x, y, c.x, c.y, c.r, true); 16 | context.clear(); 17 | context.line(x, y, tan0.x, tan0.y); 18 | context.line(x, y, tan1.x, tan1.y); 19 | context.line(c.x, c.y, tan0.x, tan0.y); 20 | context.line(c.x, c.y, tan1.x, tan1.y); 21 | context.strokeCircle(c.x, c.y, c.r); 22 | }) 23 | 24 | --------------------------------------------------------------------------------