Jump to content

Creating a very large grid with while loop


JD.

Recommended Posts

Hi, I've been trying to figure out a way to create a very large (1000x1000) grid. With each cell being 5x5, so 200 rows and 200 cols.

 

Here is the way I've been doing it:

<?

$col = 1;

while ($col <= 200) {

echo("<ul style=\"width: 1000px; height: 5px; border: 0px; margin: 0px; padding: 0px;\">");

$row = 1;

while ($row <= 200) {

	$c1 = "#ffcc00";
	$c2 = "#ffff99";
	$c3 = "#cfeef6";
	$c4 = "#b2ebc5";
	$c5 = "#ffffff";
	$c6 = "#d7ebff";
	$c7 = "#dfceb9";
	$c8 = "#b3ccc5";

	$number = rand(1,;

	$bgcolour = ${"c$number"};

	echo("<li style=\"background-color: $bgcolour; width: 5px; height: 5px; float: left; display: inline;\"></li>");

	$row = $row + 1;

}

echo("</ul>");

$col = $col + 1;

}

?>

 

It work's, however, as you can imagine it is incredibly slow loading. And browsers such as IE usually end up crashing, even on high end, fast processing PC's, let alone what might happen on an average house hold computer.

 

I've been wondering if there is a more effective method of creating the grid that I am unaware of, I will also be implementing hover boxes (overLib) over each 5x5 cell, which will display dynamic information from a database. And the background color will also be taken from the database and not be decided by the php rand(); like it currently is. So I'm thinking once I start adding more to it, it's going to run even slower than what it is now.

 

I know this is a PHP forum, but if PHP isn't the best answer would anyone think Javascript could make it run smoother? Or is it going to be impossible to create it fast and efficiently no matter what I use?

Link to comment
Share on other sites

It's not PHP that's the problem, in-fact it'd actually handle that pretty easily. It's the browser rendering the 1000x1000 grid that takes most of the time up. There's no way you're going to get around that though.

Link to comment
Share on other sites

Yeah, as MrAdam mentioned, Javascript wouldn't solve the problem. I'm not sure if this is the complete opposite of what you're after, but I figure I might as well throw the idea out there. You could maybe look into using the GD Library to generate a similar looking grid. So, rather than the browser having to render each individual cell, it would just need to render a single large image.

 

Just a thought.

 

-jm

Link to comment
Share on other sites

MrAdam is correct. If the computer that is displaying the page in a browser is having problems then it is not a PHP problem. But, if it is the PC where you are running the server (e.g. XAMPP) then it would be the PHP code. I do see inefficinecy in that code though. For example, there is no need to redefine those same 8 variables 40,000 times! (you also had the row and column loops reversed)

 

I rewrote the code and it takes the page 2-3 seconds on my PC, running as both the server and client, to load the page.

 

<html>
<head>
<style>
ul { width:1000px; height:5px; border:0px; margin:0px; padding:0px; }
</style>
</head>
<body>

<?php

$columns = 200;
$rows = 200;

$colors = array('#ffcc00', '#ffff99', '#cfeef6', '#b2ebc5', '#ffffff', '#d7ebff', '#dfceb9', '#b3ccc5');

for ($row=1; $row<=$rows; $row++)
{
   echo "<ul>\n";
   for ($col=1; $col<=$columns; $col++)
   {
      $bgcolour = $colors[array_rand($colors)];
      echo "<li style=\"background-color:$bgcolour; width:5px; height:5px; float:left; display:inline;\"></li>\n" ;
   }
   echo "</ul>\n";
}

?>

</body>
</html>

 

EDIT: You could also experiment with some alternative solutions. For example, you could create a single "template" column as a variable. Then run a loop to create each row by generating the random numbers and using printf(). WOuld be interesting to see the time differences.

Link to comment
Share on other sites

LOL, I came up with almost the same code changes -

 

<script type="text/javascript">
NavName = navigator.appName.substring(0,3);
NavVersion = navigator.appVersion.substring(0,1);
if (NavName != "Mic" || NavVersion>=4)
{
entree = new Date;
entree = entree.getTime();
}

function calculateloadgingtime()
{
if (NavName != "Mic" || NavVersion>=4)
	{
	fin = new Date;
	fin = fin.getTime();
	secondes = (fin-entree)/1000;
	window.status='Page loaded in ' + secondes + ' seconde(s).';
	document.getElementById("loadgingpage").innerHTML = "Client side page generation time " + secondes;
	}
}
window.onload = calculateloadgingtime;

</script>
<style type="text/css">
li {width: 5px; height: 5px; float: left; display: inline;}
</style>
<?php
$s1 = microtime(true);
$col = 1;
	$c[1] = "#ffcc00";
	$c[2] = "#ffff99";
	$c[3] = "#cfeef6";
	$c[4] = "#b2ebc5";
	$c[5] = "#ffffff";
	$c[6] = "#d7ebff";
	$c[7] = "#dfceb9";
	$c[8] = "#b3ccc5";
while ($col <= 200) {
echo("<ul style=\"width: 1000px; height: 5px; border: 0px; margin: 0px; padding: 0px;\">");
//echo "<ul>";
$row = 1;
while ($row <= 200) {
	$number = rand(1,;
	$bgcolour = $c[$number];
	//echo("<li style=\"background-color: $bgcolour; width: 5px; height: 5px; float: left; display: inline;\"></li>");
	echo "<li style=\"background-color: $bgcolour;\"></li>";
	$row++;
}
echo "</ul>";
$col++;
}
$e1 = microtime(true);
$t1 = $e1 - $s1;
echo "Server side page generation time: $t1";
?>
<div id="loadgingpage"></div>

 

I did find that reducing the inline css in the li tag helped by reducing the amount of characters that are being sent (edit 3.9M down to 1.7M.)

 

I have not tried it, but using a single image either as an image map or simply getting the coordinates where it is clicked seams to be like it would be a faster method.

 

edit2: Or perhaps using a separate image per row.

Link to comment
Share on other sites

EDIT: You could also experiment with some alternative solutions. For example, you could create a single "template" column as a variable. Then run a loop to create each row by generating the random numbers and using printf(). WOuld be interesting to see the time differences.

 

I didn't word that correctly. You would create a template ROW using the $columns variable, then have a loop using the $rows variable.

Link to comment
Share on other sites

Thanks for the fast response guys!!

 

With the GD Library suggestion, is there anyway you could give me an example based on the same grid layout I am after? I've never heard of that until now, I did a little Google searching, and found this: http://php.about.com/od/advancedphp/ss/gd_library_2.htm

 

I understand how it's working from the explanation, but if I copy and past that into my .php file, the image is broken and doesn't display, and I have used phpinfo() to check to see if my server has GD Library enabled and it does, unless I'm missing something?? On the last page it says about saving it as a .php file and linking to it with an img tag. But I couldn't seem to get that to work either...

 

I've tried the 2 other methods suggested and they do work considerably faster and I'm very grateful for the code changes :) but thinking about it, the GD Library seems like a more effective method to tackle the problem, only I have no idea what I'm doing, and bearing in mind the color generated will be taken from a MySQL database, so is that still possible to do so with GD Library?

 

EDIT: I always get my rows and columns mixed up! :P

Link to comment
Share on other sites

Each image on a web page must use an <img src="..." alt=""> tag. The src attribute must be a URL that results in an image being output that the browser then receives and renders on the page in the correct location. You cannot directly output binary image data on a html web page.

 

Since you would be dynamically generating one image or several images using php the URL that you put into the src attribute would be to a .php script that outputs the correct content-type header followed by the image data. If you are doing this for more than one image, you typically use a GET parameter on the end of the URL to indicate the correct image so that you can use the same .php file to generate all of the images.

 

If you were using one 1000px x 1000 px image, you would first create a 'canvas' image that size, then merge each smaller 5 x 5 image onto that 'canvas' at the correct y and x coordinate. If you were using one image per row, you would first make a 1000 x 5 'canvas', then merge each of the 5 x 5 images that make up that row.

Link to comment
Share on other sites

And here's an example that basically implements what PFMaBiSmAd just described. Again, as PFMaBiSmAd mentioned you would need to use the <img..> tag to reference this file.

 

<?php
$colors = array(
'ffff99',
'cfeef6',
'b2ebc5',
'ffffff',
'd7ebff',
'dfceb9',
'b3ccc5'
);

// convert hex to RBG values
foreach($colors as &$color) {
$color = array(
	base_convert(substr($color, 0, 2), 16, 10),
	base_convert(substr($color, 2, 2), 16, 10),
	base_convert(substr($color, 4, 2), 16, 10)
);
}

$im = imagecreatetruecolor(1000, 1000);
for($i = 0;$i <= 200;++$i) {
for($j = 0;$j <= 200;++$j) {
	list($r, $g, $b) = $colors[array_rand($colors)];
	imagefilledrectangle($im, $i * 5, $j * 5, $i * 5 + 5, $j * 5 + 5, imagecolorallocate($im, $r, $g, $b));
}
}

header('Content-type: image/png');
imagepng($im);
?>

 

It's a pretty short example and fairly self explanatory if you check the manual for the corresponding functions. If you have any questions on how it works feel free to ask. It outputs a file that is about 68KB, so that's a fairly good size difference, a lot better than 1.7MB of HTML  :P

Link to comment
Share on other sites

LOL, similar code -

main code -

<img src="image.php" alt="" width="1000" height="1000">

 

image.php code -

<?php
$img = imagecreatetruecolor(1000,1000);

$c[1] = imagecolorallocate($img,0xff,0xcc,0x00);
$c[2] = imagecolorallocate($img,0xff,0xff,0x99);
$c[3] = imagecolorallocate($img,0xcf,0xee,0xf6);
$c[4] = imagecolorallocate($img,0xb2,0xeb,0xc5);
$c[5] = imagecolorallocate($img,0xff,0xff,0xff);
$c[6] = imagecolorallocate($img,0xd7,0xeb,0xff);
$c[7] = imagecolorallocate($img,0xdf,0xce,0xb9);
$c[8] = imagecolorallocate($img,0xb3,0xcc,0xc5);

$x = 0;
while ($x < 1000) {
$y = 0;
while ($y < 1000) {
	$number = rand(1,;
	//imagefilledrectangle(resource $image , int $x1 , int $y1 , int $x2 , int $y2 , int $color)
	imagefilledrectangle($img, $x, $y, $x + 5, $y + 5, $c[$number]);
	$y+=5;
}
$x+=5;
}
header ('Content-type: image/png');
imagepng($img);
?>

Link to comment
Share on other sites

Woah, that is an insane loading time!! Really big thank you! :D

 

So, instead of using the color array(); or the imagecolorallocate(); to decide what color each 5x5 cell should be, you could fetch the hex code from a database instead of predefining a few hex codes and randomly selecting one, as long as it goes through the base_convert(); function, right? So for example:

 

base_convert(substr($db_color, 0, 2), 16, 10),
base_convert(substr($db_color, 2, 2), 16, 10),
base_convert(substr($db_color, 4, 2), 16, 10)

 

Or using the second method, as long as the 6 digit hex code was split into 3 sections in the database (as in ff is R, cc is G etc), as long as it goes through the imagecolorallocate(); function? So...

 

$color = imagecolorallocate($img,0x$db_color_r,0x$db_color_g,0x$db_color_b);

 

Would that work or am I completely getting the wrong end of the stick with this?

Link to comment
Share on other sites

Well exactly how are you intending on this working? Will you have a bunch of hex codes in the database and have them repeat over and over again in the image? If so then you can do something like this to have the colors repeat instead of being randomly selected:

 

// assume this array came from the database
$colors = array(
'ffff99',
'cfeef6',
'b2ebc5',
'ffffff',
'd7ebff',
'dfceb9',
'b3ccc5'
);

// convert hex to RBG values
foreach($colors as &$color) {
$color = array(
	base_convert(substr($color, 0, 2), 16, 10),
	base_convert(substr($color, 2, 2), 16, 10),
	base_convert(substr($color, 4, 2), 16, 10)
);
}

$numColors = sizeof($colors);
$im = imagecreatetruecolor(1000, 1000);
for($i = 0;$i <= 200;++$i) {
for($j = 0;$j <= 200;++$j) {
	//list($r, $g, $b) = $colors[array_rand($colors)];
	list($r, $g, $b) = $colors[($i * 200 + $j) % $numColors];
	imagefilledrectangle($im, $i * 5, $j * 5, $i * 5 + 5, $j * 5 + 5, imagecolorallocate($im, $r, $g, $b));
}
}
...

 

The only real difference is this line:

 

list($r, $g, $b) = $colors[($i * 200 + $j) % $numColors];

 

Which instead of randomly selecting a color from the array, picks the next color in order starting with the first color in the array and repeating once it gets to the end.

Link to comment
Share on other sites

The idea is that as users sign up, they get to select a 5x5 cell and assign it whichever color they wish too.

 

In the database, it will store that hex code, which cell (row/col) it is, and what user assigned it. So when it creates that image, instead of randomly selecting a color from an array, it fetches the chosen color to display in the correct position as it goes through the while loop, and if a certain cell hasn't been assigned a color yet, it will just be 'transparent' so to speak, until the final image has been created.

 

The reason why it stores their name is that I'll be using an area map and an overlib/hover box that will display their username and link to their profile... if all that makes sense?

 

 

EDIT: BTW, I am so very grateful of all your help, you wouldn't believe how frustrated I was getting before I knew about this image creation!!

Link to comment
Share on other sites

Oh, alright. Then you wouldn't want to be doing what I suggested in my previous post. Instead your code might look something like this:

 

$im = imagecreatetruecolor(1000, 1000);
$result = mysql_query("SELECT color FROM users"); // select information from the database
$i = 0;
while($row = mysql_fetch_assoc($result)) {
$grid_column = $i % 200;
$grid_row = floor($i / 200);
imagefilledrect(
	$im,
	$grid_column * 5,
	$grid_row * 5,
	$grid_column * 5 + 5,
	$grid_row * 5 + 5, 
	imagecolorallocate(
		$im, 
		base_convert(substr($row['color'], 0, 2), 16, 10), // assuming the color column stores a hex value that looks like this: ffff99
		base_convert(substr($row['color'], 2, 2), 16, 10),
		base_convert(substr($row['color'], 4, 2), 16, 10)
	)
);
++$i;
}

header('Content-type: image/png');
imagepng($im);

 

You can also use imagecolortransparent to make the rest of the image transparent.

 

After that you'll need to use JavaScript to get the second half working, and perhaps some AJAX.

Link to comment
Share on other sites

I don't really know much, well anything, about AJAX, so I'm hoping that would not be the only option for me :P

 

If I was to create a place in the database for each 5x5 cell, and when they select their cell and color it overwrites the information rather than creating it. Say have 200 tables (for each col respectively), and within each one, have 200 rows. Having each field left blank (until chosen), except for a unique ID number to define each specific cell.

 

Then as the while loop goes round, goes from the first, to second, to third etc and fetching the information needed and the entering that hex code into image creation code accordingly. Would that be effective and easy to create using php and SQL databases?

 

This is getting a little more complicated than I first thought, struggling to get my head around it haha

I'm hoping you understand my explanation!!

Link to comment
Share on other sites

You don't need 200 tables, and you don't need to create rows beforehand either. My previous example shows how you could fetch the data from the database. It made the assumption that the user wouldn't be able to pick their location though, it would just list the users' boxes in the order they appear in the database, but that wouldn't be too hard to change. Do you want a user to be able to select any where they want their box to be out of the 400,000 boxes?

Link to comment
Share on other sites

Yeah, so they select anywhere they want from the 40000 cells, for example, say there are no members, the whole grid would be empty, then wherever they click their mouse within the grid, that's their chosen piece. Then once they have chosen their color etc and it has been entered into the database, once that page is refreshed, that 5x5 cell would be the only one to return the color as it loops round to create the 1000x1000 image.

 

Also, not every member HAS to choose a color upon signup, so would it make much difference if the query skipped members who haven't chosen a cell? So instead of it looking for members, it just looks for hex codes and participants of the color selection. Or would it make more sense to have a members database and a grid database separate?

Link to comment
Share on other sites

Your database table (one) would hold their name or a user_id that points to their user information in a user table, their color (in your choice of storage, the hex HHHHHH value would probably be as good as any), the x, and the y coordinate -

 

id, user_id, color, x, y

 

You only have records entered into the table for users. The code that dynamically builds the image gets the color, x, and y values and simply does a imagefilledrectangle() to put each 5 x 5 block onto the master image. For the variation of the code I posted above, this would look like -

 

$query = "SELECT color,x,y FROM grid";
$result = mysql_query($query);
while($row = mysql_fetch_assoc($result)){
// id, name, color, x, y // the name would need to be output in the html
$x = $row['x'];
$y = $row['y'];	
$c = str_split($row['color'],2); // break into each hex pair
// the following offsets by 1 in each direct for a border. Subtract 1 throughout (4 places) if no border
imagefilledrectangle($img, ($x * $size) + 1 , ($y * $size) + 1, ( $x + 1 ) * $size , ( $y + 1 ) * $size, imagecolorallocate($img, "0x{$c[0]}", "0x{$c[1]}", "0x{$c[2]}"));
}
header ('Content-type: image/png');
imagepng($img);

Link to comment
Share on other sites

I haven't fully implemented it yet, but I can't see any reason as to why this approach shouldn't work, and I trust your judgement on the coding you've provided! Thank you so much for your help, I really do mean that, it's been bugging me for so long as to how I could get this to work :P

 

I'll go ahead and mark this as solved, once again, thanks guys!!

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.