Graphical pattern generation in jQuery - javascript

I am trying to build a -primitive- pattern generator in jQuery that essentially goes over 1x1 pixel and determines, depending on the value in the preceding pixel, what the one in question is going to look like.
Seeing as I am relatively proficient in jQuery but unfortunately rather oblivious to the ways of the RequestAnimFrame API, realizing the 1x1 pixel approach results in an extremely slowed down execution, making most browsers assume the script has crashed (?) even though it is in fact working, but essentially, really slow.
I have therefore tested it in bigger tile sizes because they are naturally easier to generate. My question at this point is - should I throw the jQuery approach aside and revise the script in the abovementioned API or is there a way to make it more efficient and realistically working with jQuery?
Link to code: http://codepen.io/Grovelli/pen/yepMRZ/
PS: Please excuse the trivial comments in the code
<html>
<head>
<script type="text/javascript" src="jquery-1.11.1.min.js"></script>
<script type="text/javascript">
// run once
$(function() {
//###############################################
//
// Default variables
//
//###############################################
var smallSquareWidth = 20; // size of single tile
var smallSquareHeight = 10;
var bigSquareWidth = 300; // size of tile box
var bigSquareHeight = 300;
var orientationPoint = 0; // point of orientation for pattern drawing
// Tile library
var tileArray = ["#00ff5a", "#02d24b", "#01b13f", "#039737", "#027d2d", "#02521e"];
// assign a random color from the color array tileArray
function drawTile() {
var selectedi;
// if selectedi is not 2, then roll the dice
if (orientationPoint == 0) {
// returns value of array item in question
selectedi = Math.floor(Math.random() * tileArray.length);
//alert("No 2, rolling dice");
}
// if dice return 2, then start building pattern until termination, i.e. orientationPoint being set back to 0
if (selectedi == 2) {
orientationPoint = 1; // disables the random throw above
//alert("got 2! building pattern now!");
chosenTile_Hex = tileArray[0];
orientationPoint++;
}
if (orientationPoint == 2) {
chosenTile_Hex = tileArray[1];
orientationPoint++;
return chosenTile_Hex;
} else if (orientationPoint == 3) {
chosenTile_Hex = tileArray[2];
orientationPoint++;
return chosenTile_Hex;
} else if (orientationPoint == 4) {
chosenTile_Hex = tileArray[3];
orientationPoint++;
return chosenTile_Hex;
} else if (orientationPoint == 5) {
chosenTile_Hex = tileArray[4];
orientationPoint = 0; // resets / turns off pattern building
//alert("done building pattern");
return chosenTile_Hex;
}
// return random array item
var chosenTile = selectedi; // .length is preferrable to size in this case
var chosenTile_Hex = tileArray[selectedi]; // use value of selectedi to determine which color to use for the tile i.q.
/*
// show at which position the background color value of #nChildren-1 is found within the tileArray array
//alert("found at " + (jQuery.inArray( orientationPoint, tileArray)));
var orient_pos = jQuery.inArray(orientationPoint, tileArray);
//alert(orient_pos);
//alert(tileArray[orient_pos + Math.floor(Math.random() * 3) - 2]);
$("#" + (nChildren - 1)).css("background-color", tileArray[orient_pos + Math.floor(Math.random() * 3) - 2]);
*/
//alert(chosenTile);
//alert(nextTile);
//alert(chosenTile_Hex);
return chosenTile_Hex;
}
//###############################################
// Append the main container, bigSquare to the body of the HTML file
$("<div class='bigSquare' style='position:aboslute; width:" + bigSquareWidth + "px; height:" + bigSquareHeight + "px;'></div>").appendTo("body");
/* random colors calc function
// A function to create random HEX color codes
function randomHex() {
var hexArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f"];
//creates a random hex color code
var randomHexCode = '' + hexArray[Math.floor(Math.random() * 16)] + '' +
hexArray[Math.floor(Math.random() * 16)] + '' +
hexArray[Math.floor(Math.random() * 2)] + '' +
hexArray[Math.floor(Math.random() * 16)] + '' +
hexArray[Math.floor(Math.random() * 16)] + '' +
hexArray[Math.floor(Math.random() * 16)];
// spit out a random hex code when the function terminates
return randomHexCode;
} */
// Calculate the total number of children
var nLines = bigSquareHeight / smallSquareHeight; // per column
var nPerLine = bigSquareWidth / smallSquareWidth; // and per row
// Generate as many rows as fit into .bigSquare
for (var k = 1; k <= nLines; k++) {
// per row, generate as many smallSquares as fit each row
for (var i = 1; i <= nPerLine; i++) {
// nChildren is the accumulating number of children elements in .bigSquare
var nChildren = $('.bigSquare').children().size();
// pass the accumulating number to each newly generated div
// to give each div a unique id label
$("<div class='smallSquare' id='" + nChildren + "' style='width:" + smallSquareWidth + "px; height:" + smallSquareHeight + "px; float:left;'></div>").appendTo(".bigSquare");
$("#" + nChildren).css("background-color", drawTile);
// var orientationPoint = rgb2hex($("#" + (nChildren - 1)).css("background-color"));
//alert(orientationPoint);
//alert(nChildren);
//alert(($("#" + nChildren).css("background-color")));
/*$('#' + nChildren + '').hide();
//alert(eval(k+i));
//###################################
// saving both k and i in a closure context to reuse them in INNER functions, e.g. setTimeout
(function(nChildren) {
setTimeout(function() {
$('#' + nChildren + '').fadeIn('slow');
//alert('#'+''+k+i+'');
}, nChildren * 18);
})(nChildren);*/
// first for loop END
}
// second for loop END
}
//the code below returns the number of child elements of bigSquare
//alert($('.bigSquare').children().size());
setInterval(function() {
//alert("interval");
for (var h = 0; h <= nChildren; h++) {
//alert(h);
//alert(randomHex());
//$('#'+h+'').css('background-color',randomHex);
}
}, 2000);
//alert($(".bigSquare").children(1).width());
//for centering purposes...
function centerElement() {
//resetting the margin properties each time the function is called
// to avoid margin addups
$('.bigSquare').css('margin-left', "0");
$('.bigSquare').css('margin-top', "0");
var getPageWidth = $(window).width();
var getPageHeight = $(document).height(); //alert(getPageHeight);
var centerPosX = getPageWidth / 2 - bigSquareWidth / 2;
var centerPosY = getPageHeight / 2 - bigSquareHeight / 2;
//alert(centerPosX);
//alert(centerPosY); CAREFUL ENABLING THIS AND RESIZING
// MIGHT FREEZE FF ON MACs
$('.bigSquare').css('margin-left', centerPosX + "px");
//ENABLE THIS FOR VERTICAL POSITIONING
$('.bigSquare').css('margin-top', centerPosY + "px");
//alert("resized");
}
centerElement();
//refresh on resize
//$(window).bind('resize', location.reload());
});

Related

Generate "Unique" 5 digits ID with javascript (99999 combinations) in random order

I want to generate an Unique 5 digits ID + 784 at the begining, the constraint, I can execute the script only one time, and I have to avoid the first 100 numbers so It can't be 00100 and lower. Since I use timestamp and I can execute only my script one time how I can handle this ?
I did this it's maybe dumb but at least I tried.
ConcatedID();
function ConcatedID()
{
var uniqID = checkProtectedRange();
if (checkProtectedRange())
{
var BarcodeID = 784 + uniqID;
return BarcodeID;
}
else
checkProtectedRange();
}
function checkProtectedRange()
{
var uniqueID = GenerateUniqueID();
var checkRange = uniqueID.substr(uniqueID.length - 3);
var checkRangeINT = parseInt(checkRange);
if (checkRangeINT <= 100)
return (false);
else
return (true);
}
function GenerateUniqueID()
{
var lengthID = 5;
var timestamp = + new Date();
var ts = timestamp.toString();
var parts = ts.split("").reverse();
var id = "";
var min = 0;
var max = parts.length -1;
for (var i = 0; i < lengthID; ++i)
{
var index = Math.floor(Math.random() * (max - min + 1)) + min;
id += parts[index];
}
gs.log('Generate ID ' + id);
return id;
}
Without being able to track previously used IDs, you're left with chance to prevent duplicates. Your shenanigans with Date doesn't really change that. See the birthday problem.
Given that, just follow the most straight-forward method: Generate a random string consisting of five digits.
function GenerateUniqueID() {
return ('0000'+(Math.random() * (100000 - 101) + 101)|0).slice(-5);
}
Or, if you want just the final integer with constraints applied:
function GenerateUniqueID() {
return (Math.random() * (78500000 - 78400101) + 78400101)|0;
}

Issue with for loops and recalling functions

I am making a simple battleships game where I press a button, and three boats are placed randomly. The first function placeBoat() calls the selectPos() function (which chooses a coordinate), then the placeBoat() function verifies if there is a boat there, using the property boatHitBox. If it passes the test, then buildBoat() and hitBox() are run, which places boat tiles and boat hitboxes respectively. The problem I have is that often, the boats are either one tile too short, or they are not placed at all. I think it is a problem with the hitBox() function, although I'm not sure... can anyone help?
var xPos;
var xBoxPos;
var yPos;
var boatBoxPos;
var count;
var boatPos;
var hasBoat;
var boatHitBox;
var boatGrid = {
placeBoat : function() {
for (boatNum = 1; boatNum < 4; boatNum++) {
console.log("boat placed now");
this.selectPos();
if (document.getElementById(boatPos).boatHitBox == 1) {
console.log("FAILED!!!!!!");
boatNum = 1;
this.clearTable();
}
else {
this.buildBoat();
}
}
},
selectPos : function() {
xPos = Math.floor(Math.random() * 8 + 1);
yPos = Math.floor(Math.random() * 10 + 1);
boatPos = "cell_" + xPos + "_" + yPos;
},
hitBox : function() {
for (count = 1; count < 5; count++) {
boatBoxPos = "cell_" + xBoxPos + "_" + yPos;
xBoxPos = xBoxPos + 1;
document.getElementById(boatBoxPos).boatHitBox = 1;
document.getElementById(boatBoxPos).innerHTML = "X";
}
},
buildBoat : function() {
for (boatLen = 1; boatLen < 4; boatLen++) {
xBoxPos = xPos - 2;
this.hitBox();
boatPos = "cell_" + xPos + "_" + yPos;
xPos = xPos + 1;
document.getElementById(boatPos).hasBoat = 1;
document.getElementById(boatPos).style.backgroundColor = "brown";
console.log("placed one tile");
}
},
xBoxPos = xPos - 2;
subtracts 2 from xPos which starts out as a random number in the range 1 to 8. So randomly xBoxPos can take on values such as -1 and 0 when calculated. This generates cell id values (in boatPos) like "cell_-1_7". If you don't have negative x coordinate cells on the board, this crashes
document.getElementById(boatPos).hasBoat
in the boardGrid.buildBoat function, leaving the board partially built. This is not to say other code problems will not reveal themselves but this one appears to need attention.
It is difficult to know exactly what is happening without knowing the dimensions of your grid (accompanying html). However...
There are three loops in your code sample. Two of them go up to three (<4) and the hitBox goes up to four (<5).
Change the hitBox count to the same value as the boatLen.

How can I generate n amount of colors within a range of two given colors?

I am trying to dynamically calculate a range of colors for a pie chart in JavaScript. I need the colors to step gradually from the Start Blue to the End Blue, and given the fact that I don't know how many pie slices there will be, I need to calculate it based on the value of a variable: n.
I found a great answer to this question in Java here: How to dynamically compute a list of colors?
But I'm not 100% sure how this translates into JavaScript. I also need them to be in order (lightest to darkest / darkest to lightest) in an array format.
var colorArray = [];
var startColor = #18AED5;`
var endColor = #1E1E26;`
// Calculate Colors...`
If anyone could help, I'd greatly appreciate it.
Hello I created a little Demo
JS:
var div = document.getElementById('div');
var a = b = c = 0;
setInterval(function(){
div.style.backgroundColor = 'rgb('+a+','+b+','+c+')';
a+=1;
b+=2;
c+=3;
}, 50);
It's easier to handle the dynamic color change with rgb() instead of the hex.
Update:
I was able to do as you wanted. To go from one color to another. Demo2
var div = document.getElementById('div');
function hexToRgb(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
var dynamicColorChange = function (start, end, target) {
var startRGB = hexToRgb(start),
endRGB = hexToRgb(end),
si = setInterval(function () {
if (startRGB.r < endRGB.r) {
startRGB.r += 1;
}else if(startRGB.r > endRGB.r){
startRGB.r -= 1;
}
if (startRGB.g < endRGB.g) {
startRGB.g += 1;
}else if(startRGB.g > endRGB.g){
startRGB.g -= 1;
}
if (startRGB.b < endRGB.b) {
startRGB.b += 1;
}else if(startRGB.b > endRGB.b){
startRGB.b -= 1;
}
target.style.backgroundColor = 'rgb(' + startRGB.r + ',' + startRGB.g + ',' + startRGB.b + ')';
if (startRGB.r == endRGB.r && startRGB.g == endRGB.g && startRGB.g == endRGB.g) {
clearInterval(si);
}
}, 50);
console.log(startRGB);
console.log(endRGB);
};
dynamicColorChange('#18AED5', '#1E1E26', div);

EaselJS Bitmaps not showing

I'm having an issue where bitmaps seem to be loading after they are created and added to the stage. You can see all this code working right here. You can look at the console log for the log statements I'm getting (also shown below).
I create the bitmaps in a function here (I know this code is working properly -- you don't have to read this but note how game.update is set to true at the end).
for (i = -r; i <= r; i++){
var min = Math.max(-r, -r - i);
var max = Math.min(r, r - i);
for (j = min; j <= max; j++){
k = -1 * (i + j);
//var a = length)Math.floor(Math.random() * t_imgs.;
//var a = mountain;
var a;
if(game.grid[i][j].height == null){
a = mountain;
} else if (game.grid[i][j].height > .5){
a = plain;
} else{
a = water;
}
var b = Math.floor(Math.random() * (game.t_imgs[a].length ));
if(game.t_imgs[a][b] == null){
console.log("NULL:", a, b);
}
bitmap = new createjs.Bitmap(game.t_imgs[a][b]);
container.addChild(bitmap);
bitmap.regX = game.hexwidth/2;
bitmap.regY = game.hexheight/2;
bitmap.x = game.xcenter + game.hexwidth * .75 * i;
bitmap.y = game.ycenter + game.hexheight * .5 * (j - k);
game.grid[i][j].termap = bitmap;
}
}
document.getElementById("loader").className = "";
createjs.Ticker.addEventListener("tick", tick);
console.log("finished loading images and creating bitmaps. Game update set to true");
game.update = true;
}
My problem lies in the tick function, which is called 60 times a second. I only want the map to update if something is changed (when game.update == true).
function tick(event) {
if(game.update == true){
game.update = false;
var i,j,k;
var r = game.radius;
for (i = -r; i <= r; i++){
var min = Math.max(-r, -r - i);
var max = Math.min(r, r - i);
for (j = min; j <= max; j++){
k = -1 * (i + j);
if(game.grid[i][j].termap != null){
game.grid[i][j].termap.x = game.xcenter + game.hexwidth * .75 * i;
game.grid[i][j].termap.y = game.ycenter + game.hexheight * .5 * (j - k);
} else {
console.log("A bitmap is null.");
game.update = true; //still needs to be updated
}
}
}
game.stage.update(event);
console.log("UPDATED!");
}
}
game.update is only set to true after all the bitmaps are created. As you can see if you load the website, (here is the link again), the console log shows all the bitmaps to be created before the stage is updated. This is also shown in the console print statements. If you click in the center of the screen and drag your mouse, you can force an update by moving the map, which changes game.update to true and properly forces the stage to update. I can't figure out why the bitmaps aren't showing immediately, as the game.update is clearly called only after they are created and added to the stage. Bitmaps do have a visible property, but the default value is true anyways.
I answered this here
http://community.createjs.com/discussions/easeljs/5440-bitmaps-not-showing-immediately-after-being-loaded
The short answer is that the images are created, but not actually loaded. They can be added to the DOM to be considered with the document.load (which I believe was the poster's solution), but a better approach is to properly preload them after the document load, using something like PreloadJS (http://createjs.com/preloadjs).

Does javascript have a blit or memcpy command?

I've build my own flip command and, well its slow and takes forever. I would like to know if javascript has a blit or memcpy style command. Right now I'm going through item by item with for loops to do a copy and it takes "forever".
Here is a example use of my flip function. I'm running 3 layers, only 1 if full height, with 3 simple animations and the fps it topped out at about 35 FPS. Ideally 3 layes should be topped out at far far higher FPS, in the 200+ range I would expect.
v:36.8 l0:36.8 l1:57.8 l2:36.8 The layer's FPS are the rendering to their buffers, the v is the rendering to the canvas with the flip function. (These FPS are from Chrome on a mac)
v = the screen update, the main flip function listed below.
l0 = The bottom fire, its a full height layer
l2 = The static noise, its a 1/2 height layer
l3 = The top fire, its a 1/4 height layet
Imagine having 9 or 10 layers, the FPS would drop like a stone. In FF version 12 its already unusable... not even double digit FPS rates. Opera is at least double digets.
v:4.2 l0:4.2 l1:4.2 l2:4.2 (FF 12 OSX)
v:15.5 l0:15.5 l1:15.5 l2:15.5 (Opera latest OSX)
My flip function
flip : function() {
var fps = '';
// Combine the layers onto the back buffer
for (var l = 0; l < this.layers.length; l++)
{
fps += 'l' + l + ':' + this.layers[l].fps.toFixed(1) + ' ';
var layerWidth = this.layers[l].options.width;
var layerHeight = this.layers[l].options.height;
for (var x = 0; x < layerWidth; x++)
{
for (var y = 0; y < layerHeight; y++)
{
var index = (y*this.layers[l].options.width + x)*4;
var r = this.layers[l].buffer[index+0];
var g = this.layers[l].buffer[index+1];
var b = this.layers[l].buffer[index+2];
var a = this.layers[l].buffer[index+3];
if (r|g|b|a != 0) {
this.buffer.data[index+0] = r;
this.buffer.data[index+1] = g;
this.buffer.data[index+2] = b;
this.buffer.data[index+3] = a;
}
}
}
}
fps = 'v:' + this.fps.toFixed(1) + ' ' + fps;
this.$fps.html(fps);
// blit the buffer
this.context.putImageData(this.buffer, 0, 0);
// Calculate fps
var now = new Date;
var thisFrameFPS = 1000 / (now - this.last);
this.fps += (thisFrameFPS - this.fps) / 50;
this.last = now;
var t = this;
setTimeout(function() {t.flip.apply(t);}, this.speed);
}
There is a memcpy.js that uses Typed​Array​.prototype​.subarray() if available.
The browser support is good and even IE10 have subarray.
function memcpy (src, srcOffset, dst, dstOffset, length) {
var i
src = src.subarray || src.slice ? src : src.buffer
dst = dst.subarray || dst.slice ? dst : dst.buffer
src = srcOffset ? src.subarray ?
src.subarray(srcOffset, length && srcOffset + length) :
src.slice(srcOffset, length && srcOffset + length) : src
if (dst.set) {
dst.set(src, dstOffset)
} else {
for (i=0; i<src.length; i++) {
dst[i + dstOffset] = src[i]
}
}
return dst
}
Your code can be improved, but I doubt that the speedup will be significant.
Here's what I came up with, note it is untested. I have assumed that the order of processing the layers is not significant, if it is replace the first for loop with your version.
function flip () {
var fps = '';
// Combine the layers onto the back buffer
for (var l = this.layers.length; l--;) {
fps += 'l' + l + ':' + this.layers[l].fps.toFixed (1) + ' ';
var layerWidth = this.layers[l].options.width;
var layerHeight = this.layers[l].options.height;
for (var index = 0, x = layerWidth; x--;) {
for (var y = layerHeight; y--; index += 4) {
var r = this.layers[l].buffer[index+0];
var g = this.layers[l].buffer[index+1];
var b = this.layers[l].buffer[index+2];
var a = this.layers[l].buffer[index+3];
if (r|g|b|a != 0) {
this.buffer.data[index+0] = r;
this.buffer.data[index+1] = g;
this.buffer.data[index+2] = b;
this.buffer.data[index+3] = a;
}
}
}
}
};
On the assumption that the r,g,b and a are all 8 bit quantiites you could consider packing them into a single int. That would reduce the processing in the inner loop. Even more efficient would be to use the new arrayBuffer facilities

Categories