vertically justify the created pattern - php

I have used PHP-Imagick to create a patterned-image of repeated circles, like the given example-image above. Below is the code that created the image above. I have explicitly commented the code and the variable names are kept pretty verbose in order to know whats going on.
$canvas = new Imagick();
$cw = 700; // user provided width
$ch = 300; //user provided height
$hrzntl_c = 10; //user provided - number of horizontal circles - min 2 and max 200
$c_diamtr = $cw / $hrzntl_c; //set the diameter of circle
$c_radius = $c_diamtr /2; //set the radius of circle
$vrtcl_c = $ch / $c_diamtr; //calculated number of vertical circles.
$canvas->newImage( $cw, $ch, new ImagickPixel( "seagreen" ) ); //create a canvas
$draw = new ImagickDraw(); // create a draw object
$draw->setFillColor( new ImagickPixel( "white" ) ); // set the fill color
for ($i = 0; $i <= $hrzntl_c; $i++){ //loop horizontal
for ($j = 0; $j <= $vrtcl_c; $j++){ //loop vertical
$draw->ellipse( $c_radius + $c_diamtr * $i, $c_radius + $c_diamtr * $j, $c_radius, $c_radius, 0, 360 );
}
}
$canvas->drawImage( $draw ); // render the circles to the canvas
$canvas->setImageFormat( "png" ); // set the image format to png
header("Content-Type: image/png"); // Output the image
echo $canvas;
ISSUE : Now you have got an idea that the circles are designed to be horizontally justified. I need help to justify the vertical circles as well. Look at the bottom of the example image and you can see that circles are indeed not justified. By "justified", I mean if you see a half/full circle towards top then you should see half/full a circle at the bottom too. Like this perfect example.

Calculate an offset based on the image height and the circle count. Btw, you should adapt your count calculation:
$vrtcl_c = ceil($ch / $c_diamtr);
$verticalOffset = ($ch - $vrtcl_c * c_diamtr) / 2;
Then, shift your drawings:
for ($j = 0; $j < $vrtcl_c; $j++){ //loop vertical
$draw->ellipse( $c_radius + $c_diamtr * $i,
$c_radius + $c_diamtr * $j + $verticalOffset,
$c_radius, $c_radius, 0, 360 );
}

Related

I need to figure out how much paint is needed to cover an area, I'm having problems with it. Help me or give me an idea

$width = 10; //width
$length = 10; //Length
$door = 2; // Door area
$square = $width * $length - $door; //Total area to be painted
$perMeter = 1; //Where $w is the amount of paint to paint 1m2
$total = $square * $perMeter; //The total amount of paint is sufficient for painting.
$containers = array(5,12,18,4,8,1); //The values for the number of containers are in liters.
How many barrels will be needed 5,12,18,4,8,1 (will gradually get from the largest part to the smallest part)
The whole file can look like that
<?php
declare(strict_types=1);
$width = 10; //width
$length = 10; //Length
$door = 2; // Door area
$square = $width * $length - $door; //Total area to be painted
$perMeter = 1; //Where $w is the amount of paint to paint 1m2
$total = $square * $perMeter; //The total amount of paint is sufficient for painting.
echo 'Total barrels needed: ' . $total . "barrels\n";
$containers = array(5,12,18,4,8,1); //The values for the number of containers are in liters.
rsort($containers);
$counter = 0;
while ($counter < count($containers)) {
$total -= ($amount = intdiv($total, $containers[$counter])) * $containers[$counter];
echo sprintf("%d barrel container - %d\n", $containers[$counter], $amount);
$counter++;
}
Look at the variable names, meaningful variable names are very important, if you want your code to be supported easily. And of course you can change the value of variables.

PHP-Imagick : Create a circle with 3-Dimensional effect

I am using PHP-Imagick and am trying to create a circle that would appear as a 3-Dimensional object eg. a ball. Given above is a rough example of what I am trying to achieve. Given below is a crude attempt of what I have gotten so far.
Below is the code that produced the image above. I need help to solve two obvious mistakes in the image. First, there is this another circle protruding from the highlight area. Second, the gradient is really bad around left of the circle.
$background = new Imagick();
$background->newImage(800,560,new ImagickPixel('#212121'));
$orb = new ImagickDraw();
for($i = 250; $i > 0; $i--){ //create the biggest circle first and then keep painting smaller circles over it.
$orb->push();
$blue = 350 - $i > 255 ? 255 : 350 - $i;
$red = 250 - $i;
$green = 250 - $i;
$color = 'rgba(' .$red. ',' .$green. ',' .$blue. ', 1)';
$orb->setFillColor($color);
$orb->ellipse( $i + 150 < 260 ? 260 : $i + 150,
$i + 30 < 210 ? 210 : $i + 30,
$i,
$i,
0, 360 );
$orb->pop();
}
$background->drawImage($orb);
$background->setImageFormat( "png" ); // set the image format to png
header("Content-Type: image/png"); // Output the image
echo $background;
I realize that the code is unscalable with fixed values and everything but this is the best I could do. Please help me correct it.

Border color of pattern image

I am working on image manipulation script.
I want to change outline color of the pattern image, not fill up pattern with specific color.
I have tried different method of GD library. I could fill up color of the pattern but could not set outline color of the pattern.
Please let me know if any one has solution for the same.
Thanks in advance.
Try something like this:
// Draw a border
function drawBorder(&$img, &$color, $thickness = 1)
{
$x1 = 0;
$y1 = 0;
$x2 = ImageSX($img) - 1;
$y2 = ImageSY($img) - 1;
for($i = 0; $i < $thickness; $i++)
{
ImageRectangle($img, $x1++, $y1++, $x2--, $y2--, $color);
}
}
Credit where due
You can read about imagerectangle() on the PHP man page. The key point here is that $color must be "A color identifier created with imagecolorallocate()."

How to calculate thickness of an object in PNG file using PHP GD library

I have a PNG frame and I want to know its thickness. I am able to calculate the width/height of the image itself.
$frame = imagecreatefrompng('frame.png');
// get frame dimentions
$frame_width = imagesx($frame);
$frame_height = imagesy($frame);
But can't figure out a way to calculate thickness of frame, please see image below so see what I mean.
Any suggestions?
From the last answer it shows that there's no objects in a raster image file. However, you can do it by searching the first occurrence of transparent colour and the first occurrence of the non-transparent colour and calculate the distance of them (assumes that your image's blank area are all transparent).
Example code:
<?php
$img = imagecreatefrompng('./frame.png');//open the image
$w = imagesx($img);//the width
$h = imagesy($img);//the height
$nonTransparentPos = null;//the first non-transparent pixel's position
$transparentPos = null;//the first transparent pixel's position
//loop through each pixel
for($x = 0; $x < $w; $x++){
for($y = 0; $y < $h; $y++){
$color = imagecolorsforindex($img,imagecolorat($img,$x,$y));
if($color['alpha'] < 127 && $nonTransparentPos === null){
$nonTransparentPos = array($x,$y);
}
if($color['alpha'] === 127 && $transparentPos === null){
$transparentPos = array($x,$y);
}
}
//leave the loop if we have finished finding the two values.
if($transparentPos !== null && $nonTransparentPos !== null){
break;
}
}
$length = $transparentPos[0]-$nonTransparentPos[0];//calculate the two point's x-axis distance
echo $length;
?>
There aren't any objects in a PNG file. You can only get a color (with a transparency) by coordinates with imagecolorat() & imagecolorsforindex().

Procedurally generating a texture

I'm trying to figure out a script that will generate a texture (which can then be multiplied by a grayscale image to "apply" it). So far my method involves seeding the RNG, then randomly generating a 8x8 matrix of integers in the range [0,3], then scaling up that matrix to a 256x256 image using some level of interpolation.
Here's an example output (seed value 24):
(source: adamhaskell.net)
On the left is the matrix scaled with nearest-neighbor interpolation. On the right is my attempt at bilinear interpolation. For the most part it seems okay, but then you get structures like near the middle-left where there are two diagonally-adjoining orange squares faced with two diagonally-adjoining red squares, andthe result is no interpolation for that area. Additionally, it's being treated more like a heatmap (as shown by the abundance of orange in the top-left corner) and that's causing more problems.
Here's the code I have for my "bilinear interpolation":
<?php
$matrix = Array();
srand(24);
$dim = 256;
$scale = 32;
for($y=0;$y<=$dim/$scale;$y++) for($x=0;$x<=$dim/$scale;$x++) $matrix[$y][$x] = rand(0,3);
$img = imagecreate($dim,$dim);
imagecolorallocate($img,255,255,255);
$cols = Array(
imagecolorallocate($img,128,0,0),
imagecolorallocate($img,128,64,32),
imagecolorallocate($img,128,128,0),
imagecolorallocate($img,64,64,64)
);
for($y=0;$y<$dim;$y++) {
for($x=0;$x<$dim;$x++) {
$xx = floor($x/$scale); $yy = floor($y/$scale);
$x2 = $x%$scale; $y2 = $y%$scale;
$col = $cols[round((
$matrix[$yy][$xx]*($scale-$x2)*($scale-$y2)
+ $matrix[$yy][$xx+1]*$x2*($scale-$y2)
+ $matrix[$yy+1][$xx]*($scale-$x2)*$y2
+ $matrix[$yy+1][$xx+1]*$x2*$y2
)/($scale*$scale))];
imagesetpixel($img,$x,$y,$col);
}
}
header("Content-Type: image/png");
imagepng($img);
exit;
In reality, this may be a bit of an XY Problem. What I'm specifically trying to do is generate "fur patterns" for creatures in a game I'm planning. In particular I want to be able to have it so that breeding mixes elements from the two parents (be it colour or elements of the pattern), so just having a random seed won't really cut it. Ideally I need some kind of vector-based approach, but I'm way out of my depth there so any help would be very much appreciated.
A couple things come to mind:
You are not interpolating the color values. To expand on zakinster's comment, you are interpolating the color indices, and then rounding to the nearest one. One effect of this is that you wind up with a swath of yellow (index 2) in between orange (index 1) and gray (index 3) areas. If you interpolated the color values instead, you would wind up with, perhaps, grayish orange?
You have more yellow and orange, and less red and gray in the final image. This is because of using round() to snap to a color index. Your calculation (before round()) may produce floats evenly distributed between 0 to 3, but rounding doesn't preserve it.
So, here are some suggestions:
If you are not limited to 4 colors, use more. Interpolate the color values (i.e. (128,0,0) mixed with (64,64,64) produces (91,32,32)) rather than the indices.
If you are limited to just those 4 colors, try some kind of dithering. A simple approach, with minimal changes to your code, would be to add some randomness to the color index that is chosen. So, instead of round(...), do something like this: say your calculation produces the value 1.7. Then, round to up to 2 with a 70% probability, and down to 1 the other 30%. This will blend the colors, but it may produce a very noisy image. If you are prepared to change your code more substantially, check out Floyd-Steinberg dithering.
I know it is old question, and answer from #markku-k is correct, anyway I have similar problem and here is my modified code for the question
several notices:
it produces 2 images in one, to show "original matrix" and result
it uses 8x8 matrix to produce result, but actual matrix is 10x10 to cover borders
it uses color to color index algorithm base on simple delta, it works ok for me
here is the code:
<?php
$matrix = array();
$dim = 256;
$scale = 32;
for($y=0; $y<=9; $y++)
{
$matrix[$y] = array();
for($x=0; $x<=9; $x++)
{
$same = false;
do
{
$matrix[$y][$x] = mt_rand(0, 3); // do not use rand function, mt_rand provide better results
if ( ($x>0) && ($y>0) ) // check for checkers siatuion, where no colors are preferable and produce 90 degree angles
{
$c1 = $matrix[$y-1][$x-1];
$c2 = $matrix[$y][$x];
$c3 = $matrix[$y-1][$x];
$c4 = $matrix[$y][$x-1];
$same = ( ($c1==$c2) && ($c3==$c4) );
}
} while ($same);
}
}
$img = imagecreate($dim*2 + 32*4, $dim + 32*2);
$colorsRGB = array(0x800000, 0x804020, 0x808000, 0x404040);
$cols = Array(
imagecolorallocate($img,128,0,0), // red
imagecolorallocate($img,128,64,32), // orange
imagecolorallocate($img,128,128,0), // yellow
imagecolorallocate($img,64,64,64), // gray
imagecolorallocate($img,0,0,0), // black, just to fill background
);
imagefilledrectangle($img, 0, 0, $dim*2 + 32*4 - 1, $dim + 32*2 - 1, $cols[4]);
function mulclr($color, $multiplicator)
{
return array(($color>>16) * $multiplicator, (($color>>8)&0xff) * $multiplicator, ($color&0xff) * $multiplicator);
}
function addclr($colorArray1, $colorArray2)
{
return array($colorArray1[0]+$colorArray2[0], $colorArray1[1]+$colorArray2[1], $colorArray1[2]+$colorArray2[2]);
}
function divclr($colorArray, $div)
{
return array($colorArray[0] / $div, $colorArray[1] / $div, $colorArray[2] / $div);
}
function findclridx($colorArray, $usedColors)
{
global $colorsRGB;
$minidx = $usedColors[0];
$mindelta = 255*3;
foreach ($colorsRGB as $idx => $rgb)
{
if (in_array($idx, $usedColors))
{
$delta = abs($colorArray[0] - ($rgb>>16)) + abs($colorArray[1] - (($rgb>>8)&0xff)) + abs($colorArray[2] - ($rgb&0xff));
if ($delta < $mindelta)
{
$minidx = $idx;
$mindelta = $delta;
}
}
}
return $minidx;
}
for($y=0; $y<($dim+64); $y++)
{
for($x=0; $x<($dim+64); $x++)
{
$xx = $x>>5;
$yy = $y>>5;
$x2 = ($x - ($xx<<5));
$y2 = ($y - ($yy<<5));
imagesetpixel($img, $x, $y, $cols[$matrix[$yy][$xx]]);
if ( ($xx>0) && ($yy>0) && ($xx<=8) && ($yy<=8) )
{
$color1 = $colorsRGB[$matrix[$yy][$xx]];
$color2 = $colorsRGB[$matrix[$yy][ ($xx+1) ]];
$color3 = $colorsRGB[$matrix[ ($yy+1) ][$xx]];
$color4 = $colorsRGB[$matrix[ ($yy+1) ][ ($xx+1) ]];
$usedColors = array_unique(array($matrix[$yy][$xx], $matrix[$yy][ ($xx+1) ], $matrix[ ($yy+1) ][$xx], $matrix[ ($yy+1) ][ ($xx+1) ]));
$a1 = mulclr($color1, ($scale-$x2)*($scale-$y2));
$a1 = addclr($a1, mulclr($color2, $x2*($scale-$y2)));
$a1 = addclr($a1, mulclr($color3, ($scale-$x2)*$y2));
$a1 = addclr($a1, mulclr($color4, $x2*$y2));
$a1 = divclr($a1, $scale*$scale);
$clrIdx = findclridx($a1, $usedColors);
$col = $cols[$clrIdx];
imagesetpixel($img, $dim+$x+32*2, $y, $col);
}
}
}
header("Content-Type: image/png");
imagepng($img);
exit;
here is sample result:

Categories