randomly generating colors with php - php

So I'm working on making my header change color everyday, and I was attempting to create this using a random color. There are 2 colors in the header and I am making them complimentary colors. the first color is generated randomly, and then the second is modified by changing the Hue via 150`. The problem is when certain colors are chosen, they could be either too vibrant or dark. I have a check running so that I can slightly control the brightness value, but there are still some colors that are too bright ( for instance extreme yellows ). I'l post my code below. Any help or suggestions is appreciated! Thanks!
// grab a random color on hue
$h = rand(0,360);
// color values 50-120 tend to be extremely bright,
// make adjustments to the S and L accordingly
// a better solution is available?
if ($h > 50 && $h < 120) {
$s = rand(60,80);
$l = rand(30,50);
} else {
$s = rand(60,90);
$l = rand(38,63);
}
// declare string to place as css in file for primary color
$randomColor = "hsl(". $h .",". $s ."%,". $l ."%)";
// declare degree for secondary color (30 = analogous, 150 = complimentary)
$degree = 150;
// point to secondary color randomly on either side of chart
$bool = rand(0,1);
if ($bool) {
$x = $degree;
} else {
$x = -$degree;
}
// set value of the new hue
$nh = $h + $degree;
// if the new hue is above 360 or below 0, make adjustments accordingly
if ($nh > 360) {
$nh -= 360;
}
if ($nh < 0 ) {
$nh = 360 - $nh;
}
// set the secondary color
$secondaryColor = "hsl(". abs($h + $x) .",". $s ."%,". $l ."%)";
This seems very simple and I'm sure there is a better method. I looked around, but all I noticed were the basic formula's via degrees for the hue etc. Thanks again!

This is really more of a question of which colors you deem acceptable for viewing. This certainly isn't an optimal solution but it's an approach that is readable at least (it's also slightly more random than your original, if you even care about that):
function randColor() {
return array( rand(0,360), rand(0,100), rand(0,100) );
}
function isAcceptableColor($colorArr) {
// return true if the color meets your criteria
}
do {
$color = randColor();
} while ( ! isAcceptableColor($color) );

Related

PHP Image Zoom - Exponential scale

Iam trying to calculate a zoom Effect beetween 12 Images. Every Image is 100% larger then the one before. Its geting near to perfect, but there is only a issue at the transition beetween the images. It isn't fluid zoom beetween each image.
Please see the video: http://youtu.be/dUBbDjewpO0
I think the Exponential expression pow() isnt coorect for some reason.
Here is the PHP script, but i cant find the issue:
<?php
$imageFiles=array(
'1.jpg',
'2.jpg',
'3.jpg',
'4.jpg');
$targetFrameRate=$targetDuration='18';
$imageCount = count($imageFiles);
$totalFrames = ($targetFrameRate*$targetDuration);
$sourceIndex = 0;
$firstIndex = 1;
$lastIndex = $totalFrames; //==total frames
$currentScale = 1;//image scaling for first scale
$deltaScale = ((($imageCount-1)*($scaleFactor-$currentScale))/$totalFrames);
for ($i=$firstIndex; $i<=$lastIndex; $i++) {
// prepare filename
$filename = createImageFilename($i, $imageType);
// determine source..
if ($i == $firstIndex) {
$newSourceIndex = 0;
}
else if ($i == $lastIndex) {
$newSourceIndex = ($imageCount-1);
}
else {
$newSourceIndex = intval(($i*($imageCount-1))/$totalFrames);
}
// create frame..
if ($newSourceIndex != $sourceIndex) {
$sourceIndex = $newSourceIndex;
$currentScale = pow($scaleFactor, $sourceIndex);
$nextScale = pow($scaleFactor, ($sourceIndex+1));
$deltaScale = ((($imageCount-1)*($nextScale-$currentScale))/$totalFrames);
copyImage($imageFiles[$sourceIndex],
sprintf('%s/%s', $outputDir, $filename),
$imageWidth,
$imageHeight,
$imageType);
}
else {
createImage($imageFiles[$sourceIndex],
sprintf('%s/%s', $outputDir, $filename),
($currentScale/pow($scaleFactor, $sourceIndex)),
$imageWidth,
$imageHeight,
$imageType);
}
//DEBUG: buffer some values for optional debug-output
if (isDebugOutputEnabled()) {
$debug_idx[$i] = $filename;
$debug_inf[$i] = sprintf('sourceIndex=%d , scale=%01.2f<br />', $sourceIndex, $currentScale);
}
// advance..
$currentScale += $deltaScale;
}
?>
rendering is well
shell_exec('ffmpeg -f image2 -i /var/www/htdocs/image2/i%d.jpg -s 1280x720 -movflags faststart -b:v 5500k -r 18 output.flv');
The problem comes from the fact that you are adding a delta to your scale instead of multiplying it by a constant amount each frame:
$currentScale += $deltaScale;
An exponential zoom means you increase the zoom by a constant factor (not difference) for a given constant amount of time, so you need to change that line to:
$currentScale *= $deltaScale;
and also calculate $deltaScale differently:
$deltaScale = pow($nextScale / $currentScale, ($imageCount-1) / $totalFrames);
This will compute a fractional power of the scale difference between the images, so that when you multiply it with the $currentScale value $totalFrames / ($imageCount-1) times (the number of frame you render between the current scale and next scale), the result will be an increase by a factor of $nextScale / $currentScale.
Simplification:
Because the zoom is at a constant rate for the whole animation, $deltaScale is constant the whole time, so you can compute it outside the loop like this:
$deltaScale = pow($scaleFactor, ($imageCount-1) / $totalFrames);

FPDF height of a MultiCell Element

I use the FPDF library to export some document files as PDF. One document includes a list of strings which have a different length. I print all strings as $pdf->MultiCell(). Now I would like to have the current height of that MultiCell to have the same line spacing in case that they have just one line or more.
Code Example:
//MySQL Query
while($row = mysql_fetch_array($res) {
$pdf->SetXY(18, $x);
$pdf->MultiCell(80, 5, $rowr['text']); //text has one or more lines
$x = $x + 10; // Here I would prefer a solution to say: $x = $x + 2 + height of the MultiCell()
}
I had the exact same problem; I use FPDF to generate invoices and there are four cells per row with first cell being a MultiCell with varying height (default height is 5, but if order name is too long it adds another line to a total height of 10, and so on). The problem is that the remaining 3 cells have fixed height.
After looking for solutions it seems the only way is to write a complex function or use some third party tool. Since I want my app to be as light as possible I used the following approach to solve it which in my opinion is way simpler than external plugin.
Rows with details on the Invoice start at Y=95 so I use $Y=95;
before my while loop
My first cell (the MultiCell) is as follows:
$pdf->SetXY(10,$Y);
$pdf->MultiCell(60,5,$row['Name'],1,1,'L');
I use the FPDF's GetY() function to get current height and save it as H:
$H = $pdf->GetY();
If the MultiCell's height is 5 GetY() will give back 100, if the height is 10 GetY() gives back 105 and so on.
I add new variable $height:
$height= $H-$Y;
Which as result gives me precisely the height of the MultiCell.
I use $Y and $height to set current position and column height:
$pdf->SetXY(130,$Y);
$pdf->Cell(40,$height,$row['RowName'],1,1,'L');
Before finishing the while loop set give $Y the $H's value:
$Y=$H;
Entire loop looks as follows and works perfectly:
$Y= 95;
$query = mysqli_query($con,"SELECT * FROM table");
while($row = mysqli_fetch_array($query)) {
$pdf->SetXY(10,$Y);
$pdf->MultiCell(60,5,$row['ROW1'],1,1,'L');
$H = $pdf->GetY();
$height= $H-$Y;
$pdf->SetXY(70,$Y);
$pdf->Cell(60,$height,$row['ROW2'],1,1,'L');
$pdf->SetXY(130,$Y);
$pdf->Cell(40,$height,$row['ROW3'],1,1,'L');
$pdf->SetXY(170,$Y);
$pdf->Cell(30,$height,$row['ROW4'],1,1,'L');
$Y=$H;
}
If you have 2 MultiCell columns or more in each row it gets tricky but still can be solved in a similar manner.
I found interesting solution here - https://github.com/artkonekt/pdf-invoice/blob/master/src/InvoicePrinter.php#L469
$calculateHeight = new self;
$calculateHeight->addPage();
$calculateHeight->setXY(0, 0);
$calculateHeight->SetFont($this->font, '', 7);
$calculateHeight->MultiCell($this->firstColumnWidth, 3, $item['description'], 0, 'L', 1);
$descriptionHeight = $calculateHeight->getY() + $cellHeight + 2;
So, he literally create a 'temporary' PDF, add multicell, and then simply measure height (newY - oldY)
Also, keep in mind that if text goes to new line - height of cell will be = number_of_lines * $height (height passed to MultiCell as second parameter)
So, if you passed 5 as $height, and temporary PDF measure that cell will be 15, you can be sure that text will spread to 3 lines.
I'm coding in golang so I'll show some pseudo-code. I hope the accessible methods are the same in php as in golang.
There is a method called pdf.SplitLines(text, width). You will pass your string content and the desired width and it will return an array of strings that represents the lines that'll be computed to display that content.
With that its easy. In pseudo-code it could look like:
fontSize = 10;
lineHeight = 12;
targetWidth = 50;
pdf.SetFontSize(fontSize)
nLines = length(pdf.SplitLines(content, targetWidth));
multiCellHeight = nLines * lineHeight;
pdf.Multicell(targetWidth, lineHeight, content, ...)
The rendered MultiCell will have the exact same size as stored in multiCellHeight. This way you'll get the hight before rendering.
This is working because the passed height for the MultiCell is the lineHeight of each row. If you know the rows before rendering, you'll get the total height.
I'm sorry if this fails for any reason for php. Just let me know if that's the case.
Wouldn't it be easier to simply NOT use boarders when you print out the text.
You can just use $pdf->GetY(); to get the curent y value.
Then when you have printed all the text, you can use $pdf->GetY(); to get the height after each piece of text. Compare y values to see which one is the biggest.
All you need to do then is $pdf-> SetY($y); to the original y value and paint the boarders with $pdf->Cell() now that you know height and width;
That's how I'd do it.
Edit: tested it, and seems to work. Now sing with me - "you get the beeeeest of both worlds..." no?
$cellData = array();
$cellData[0] = array();
$cellData[0][0] = array();
$cellData[0][0]['text'] = 'Audiometry';
$cellData[0][0]['width'] = '47';
$cellData[0][1] = array();
$cellData[0][1]['text'] = 'Control of Noise at Work Regulations 2005.';
$cellData[0][1]['width'] = '47';
$cellData[0][2] = array();
$cellData[0][2]['text'] = 'Fit with restrictions (as detailed below) to work in a noise controlled zone.';
$cellData[0][2]['width'] = '47';
$cellData[0][3] = array();
$cellData[0][3]['text'] = 'Recommended review date: 2021-11-27
Referral: Referred to OHP';
$cellData[0][3]['width'] = '47';
setAllCellSizes($cellData, $pdf);
function setAllCellSizes($cellData, $pdf){
$y = $pdf->GetY();
$x = $pdf->GetX();
$largestCell = 0;
for($cordI = 0; $cordI < count($cellData); $cordI++){
$curX = 10;
for($cordJ = 0; $cordJ < count($cellData[$cordI]); $cordJ++){
$pdf->SetXY($curX,$y);
$pdf-> MultiCell($cellData[$cordI][$cordJ]['width'], 5, $cellData[$cordI][$cordJ]['text'], '0', 'L');
$curX = $curX + $cellData[$cordI][$cordJ]['width'];
$cellHeight = $pdf->GetY() - $y;
if($largestCell < $cellHeight){
$largestCell = $cellHeight;
}
}
$curX = 10;
for($cordJ = 0; $cordJ < count($cellData[$cordI]); $cordJ++){
$pdf->SetXY($curX,$y);
$pdf->Cell($cellData[$cordI][$cordJ]['width'],$largestCell,'',1,1,'L');
$curX = $curX + $cellData[$cordI][$cordJ]['width'];
}
}
return $cellData;
}

PHP GD basic rectangle offset maths

I'm completely stuck with something that i believe is relatively simple to solve:
In PHP I am drawing filled rectangles on top of an image (using GD) - I want a small gap between each of them - which is specified in the code (gap is the same for each box) - I loop around until i've hit the max amount of rectangles on a row (e.g max columns) - and i've managed the horizontal gap offset but for the life of me can't work out the vertical gap maths.
Here is my code in it's nasty entirety:
http://pastebin.com/MHUqi0tG
But specifically PHP accepts two sets of coordinates to make a rectangle - two for the top left (x and y) and two for the bottom right (x and y).
Here is my code for that in particular:
$left_wall_of_box_x = ( $current_col * $box_size ) + $origin_x ;
if($last_block_x != $origin_x){
$left_wall_of_box_x = $last_block_x + $gap;
}
/*verticals coord 1*/
$left_wall_of_box_y = ( $current_row * $box_size ) + $origin_y;
$right_wall_x = $left_wall_of_box_x + $box_size ;
$right_wall_y = $left_wall_of_box_y + $box_size;
imagefilledrectangle($im, $left_wall_of_box_x, $left_wall_of_box_y, $right_wall_x, $right_wall_y, $red);
$gap = 2;
$origin_x & origin_y = the place i start drawing boxes from.
Output at the moment is nicely separated boxes on the horizontal axis but vertically they are hitting each other/merging together.
Any help obviously greatly appreciated - Tearing hair out on this.
Thanks
The mistake is here:
$left_wall_of_box_y = ( $current_row * $box_size ) + $origin_y;
Should be:
$left_wall_of_box_y = ( $current_row * ($box_size + $gap_vertical)) + $origin_y;
But your code is fragile and hard to read. It's fragile as you're making your calculations more be dependent on variables that represent subtly different things, and as there's more variables, it's harder to fit in your head - which is partly why you didn't spot the error.
So, rather than doing this:
while($x <= $total_boxes_to_draw)
{
if($current_col >= $cols)
{
//reset to start of row
}
}
Write your code like this.
$finished = false;
for ($y=0; ($y<$rows) && ($finished==false) ; $y++)
{
$yPosition = $origin_y + $y * ($box_size + $spacing_vertical);
for ($x=0 ; $x<$cols && ($finished==false) ; $x++)
{
$xPosition = $origin_x + $x * ($box_size + $spacing_horizontal);
//draw box at $xPosition, $yPosition
$boxesDrawn++;
if ($boxesDrawn >= $total_boxes_to_draw){
$finished = true;
}
}
}
You are adding a gap horizontally, but you also have to add it vertically.
A simple fix is subtracting the gap from the height of the rectangles you fill:
imagefilledrectangle($im, $left_wall_of_box_x, $left_wall_of_box_y,
$right_wall_x, $right_wall_y - gap, $red);

Color coding based on number

I want to display a color between red, yellow, green depending on a number between 1 to 100.
1 being green and 100 being red, 50 being yellow. I want to basically create a gradient between that.
So far, I tried:
$r = floor(255 * ($number / 100));
$g = 255 - $r;
And it somewhat does it, but gives me brownish & dark colors, & no yellow at all.
It's because you shouldn't change both channels at once but rise R in the first half and lower G in the second.
Try a function like this:
function GreenYellowRed($number) {
$number--; // working with 0-99 will be easier
if ($number < 50) {
// green to yellow
$r = floor(255 * ($number / 50));
$g = 255;
} else {
// yellow to red
$r = 255;
$g = floor(255 * ((50-$number%50) / 50));
}
$b = 0;
return "$r,$g,$b";
}
To test it:
$output = "";
for ($i = 1; $i <= 100; $i++) {
$rgb = GreenYellowRed($i);
$output .= "<div style='background-color: rgb($rgb)'>$rgb</div>";
}
echo $output;
I've found that dealing with the HSV color model is easier than the RGB model. It helps you easily choose the color you want to work with; with RGB you'd need to understand how different values of R, G and B will combine to give you the color you want/don't want.
Also, this SO question might be useful: How can I cycle through hex color codes in PHP?
I don't know of a mathematical model for a "color curve" that passes through specified RGB color values (e.g. what you describe as green/yellow/red), which would allow you to calculate any intermediate color in that curve. In any case, a model of a function (which is what that would be) is only as good as the data points it needs to fit, so you 'd have to be much more specific than green/yellow/red to get decent results even if someone points out the math.
Remember that we are not interested in mathematical interpolation here, but rather in "color-space interpolation" (a term which I just made up) -- in other words, what would look like a "natural" interpolation to a human.
An easier solution for those of us who do not have the necessary color theory knowledge, and which I 'd suggest, is to pre-select a number of colors with a color picker tool, divide the 0-100 range into as many bands as the colors you picked, and use simple integer division to project from 0-100 to a color band.
Food for thought: Indeed, how does SO decide the color of the upvote count for comments?
Update: I just asked the above over on meta. Let's see...
After a bit of looking, none of the solutions looked pleasing. As stated above, HSV is probably the way to go, since modern browsers can render color with it just fine.
To get a good idea of the colors you are working with, check out this color wheel:
http://www.colorspire.com/rgb-color-wheel/
I want to start with blue, so I use 255 for normalization.
function temp_color($temp){
$start = 40;
$end = 85;
$normal = round(255-((($temp - $start)/($end-$start))*255));
$color = "hsl($normal, 100%, 30%);";
$span = "<span style=\"color: $color\">$temp</span>";
return $span;
}

Hex Code Brightness PHP?

I want users on my website to be able to pick a hex colour, and I just want to display white text for dark colours and black text for light colours. Can you work out the brightness from a hex code (preferably PHP)?
$hex = "78ff2f"; //Bg color in hex, without any prefixing #!
//break up the color in its RGB components
$r = hexdec(substr($hex,0,2));
$g = hexdec(substr($hex,2,2));
$b = hexdec(substr($hex,4,2));
//do simple weighted avarage
//
//(This might be overly simplistic as different colors are perceived
// differently. That is a green of 128 might be brighter than a red of 128.
// But as long as it's just about picking a white or black text color...)
if($r + $g + $b > 382){
//bright color, use dark font
}else{
//dark color, use bright font
}
I made one similar - but based on weightings of each colour (based on the C# version of this thread)
function readableColour($bg){
$r = hexdec(substr($bg,0,2));
$g = hexdec(substr($bg,2,2));
$b = hexdec(substr($bg,4,2));
$contrast = sqrt(
$r * $r * .241 +
$g * $g * .691 +
$b * $b * .068
);
if($contrast > 130){
return '000000';
}else{
return 'FFFFFF';
}
}
echo readableColour('000000'); // Output - FFFFFF
EDIT:
Small optimisation:
Sqrt is known as an expensive math operation, which is probably neglectable in most scenarios, but anyway, it could be avoided by doing something like this.
function readableColour($bg){
$r = hexdec(substr($bg,0,2));
$g = hexdec(substr($bg,2,2));
$b = hexdec(substr($bg,4,2));
$squared_contrast = (
$r * $r * .299 +
$g * $g * .587 +
$b * $b * .114
);
if($squared_contrast > pow(130, 2)){
return '000000';
}else{
return 'FFFFFF';
}
}
echo readableColour('000000'); // Output - FFFFFF
It simply doesn't apply the sqrt, instead it powers the desired cut off contrast by two, which is a much cheaper calculation
I know this is a very old topic, but for users who came from "Google Search", this link may be what they are looking for. I've searched for something like this and I think it's a good idea to post it here:
https://github.com/mexitek/phpColors
use Mexitek\PHPColors\Color;
// Initialize my color
$myBlue = new Color("#336699");
echo $myBlue->isLight(); // false
echo $myBlue->isDark(); // true
That's it.
You need to convert the RGB values to HLS/HSL (Hue Lightness and Saturation) you can then use the Lightness to determine whether you need light text or dark text.
This page has some details on how to the conversion in PHP as well as selecting complementary colour from this.
I've only just spotted that the site is an astrology site - so apologies if anyone's offended.
If you have imagemagick extension activated, you can simply create an ImagickPixel object, call setColor with your hex value, and then call getHSL() (and get the last item of the obtained array I suppose)...
I tried a different approach to this, I used HSL (hue, saturation & lightness) lightness percentage to check if the color is dark or light. (like #chrisf said in his answer)
function:
function colorislight($hex) {
$hex = str_replace('#', '', $hex);
$r = (hexdec(substr($hex, 0, 2)) / 255);
$g = (hexdec(substr($hex, 2, 2)) / 255);
$b = (hexdec(substr($hex, 4, 2)) / 255);
$lightness = round((((max($r, $g, $b) + min($r, $g, $b)) / 2) * 100));
return ($lightness >= 50 ? true : false);
}
On the return line it checks if the lightness percentage is higher than 50% and returns true otherwise false is returned. You can easily change it to return true if the color has 30% lightness and so on. The $lightness variable can return from 0 to 100 0 being the darkest and 100 being the lightest.
how to use the function:
$color = '#111111';
if ( colorislight($color) ) {
echo 'this color is light';
}
else {
echo 'this color is dark';
}

Categories