Hello Stackoverflow community,
for a little game I need to display an octagon (Like this one)
The shape adapts itself to certain values I get from the database. My Problem is, I have absolutely no idea how to launch into it. I neither know the formula for my purpose, nor I know how I could draw such a shape in PHP.
In general I'm relatively good at PHP. So I`d be happy about theoretical approaches to a solution and not necessarily code =)
Thanks in advance
Whipped this up. It calculates the coordinates for you already, but you can easily specify your own coordinates in the $vertices array (and remove the generation).
<?php
$radius = 100;
$sides = 8;
$points = array();
for ($i = 1; $i <= $sides; $i++) {
$points[] = round( $radius * cos($i*2 * pi() / $sides) + $radius ); // x
$points[] = round( $radius * sin($i*2 * pi() / $sides) + $radius ); // y
}
// Draw the image.
$im = imagecreate($radius*2 + 10, $radius*2 + 10);
$black = imagecolorallocate($im, 0, 0, 0);
$white = imagecolorallocate($im, 255, 255, 255);
imagefill($im, 0, 0, $white); // White background
imagefilledpolygon($im, $points, $sides, $black);
header('Content-type: image/png');
imagepng($im);
I can't give you a formula for this, but once you figured one out you can make use of the GD extension and draw your shape.
Google Charts API Supports this, and is fairly easy to use.
Example
Related
I need to "transform" a normal rectangle image into a "pizze slice". So this:
Must be stretched into this:
This way I lose the actual wording, so it's not 100% correct. I need it to not just mask those parts, but also "stretch" the slice so it will fit into the new triangle shape, so none of the text is lost.
My photoshopping skills are so limited, I can't even do a proper preview in photoshop that shows how it must be cut.
How would I do this using GD Image?
This should get the job done - the script is commented, so you should have no trouble understanding it.
<?php
$img = imagecreatetruecolor(600, 130);
$text = "Text that can be\nwrapped to next line";
//draw red background
imagefilledrectangle($img, 0, 0, imagesx($img), imagesy($img), 0xa00000);
//draw green text
putenv('GDFONTPATH=/usr/share/fonts/TTF');
imagettftext($img, 40, 0, 20, 50, 0x00a000, 'arial.ttf', $text);
//stretch whole thing vertically
$img = imagescale($img, imagesx($img), imagesy($img) * 2);
//compute pizza transformation
$y_arr = [];
for ($x = 0; $x < imagesx($img); $x++)
{
//compute simple "triangle" shape
$linear_y = $x * imagesy($img) / 2 / imagesx($img);
//get some variations with cos()
$cos_y = cos($x * 2 * M_PI / imagesx($img));
$cos_y *= cos($x * 2 * M_PI / imagesx($img) / 2);
$cos_y = (1 - $cos_y) * imagesy($img) / 4;
//finally combine these two
$y = ($cos_y + $linear_y * 2) / 3;
//and push the coordinate onto stack
$y_arr []= $y;
}
//create target image
$dstimg = imagecreatetruecolor(imagesx($img), imagesy($img));
//scale each column according to pizza transformation
foreach ($y_arr as $x => $y)
imagecopyresized($dstimg, $img, $x, $y, $x, 0, 1, max(1, imagesy($img) - 2 * $y), 1, imagesy($img) - 1);
//write the image to PNG file
imagepng($dstimg, 'test.png');
Result:
I'm trying to write a script that generates a PNG image from the text, size and the font it gets in the $_GET arguments, but I can't figure out how to make size of the image fit exactly to the text. I'm already using imagettfbox:
$widthPx = abs($ttfBox[2] - $ttfBox[0]);
$heightPx = abs($ttfBox[1] - $ttfBox[7]);
which probably gives me the correct measurements but when I draw my text, it gets a little bit out of bounds. For example if I try to draw an "a" using arial.ttf its at least 5 pixels out of bounds. Is there a way to draw a text of any font exactly fitting to the image without testing out?
$text = $_GET["text"];
$cmToPixel = 15.0;
$sizeCm = floatval($_GET["sizeCm"]);
$sizePx = $cmToPixel * $sizeCm;
$fontFile = "fonts/".pathinfo($_GET["font"])["filename"].".".pathinfo($_GET["font"])["extension"];
if(!file_exists($fontFile)){
die;
}
$ttfBox = imagettfbbox($sizePx, 0, $fontFile, $text);
$widthPx = abs($ttfBox[2] - $ttfBox[0]);
$heightPx = abs($ttfBox[1] - $ttfBox[7]);
$image = ImageCreate($widthPx, $heightPx);
$x = $ttfBox[0] + (imagesx($image)-$ttfBox[4] )/ 2 - 0;
$y = $ttfBox[1] + (imagesy($image) / 2) - ($ttfBox[5] / 2) - 5;
ImageRectangle($image,0,0,imagesx($image),imagesy($image), ImageColorAllocate($image,255,255,255));
imagettftext($image, $sizePx,0,$x,$y, ImageColorAllocate($image, 0, 0, 0), $fontFile, $text);
header("Content-Type: image/png");
ImagePng($image);
ImageDestroy($image);
Your calculations from the bounding box are off. This works:
<?php
/*-
* $MirOS: www/mk/ttf2png,v 1.8 2016/11/02 16:16:26 tg Exp $
*-
* Copyright (c) 2009, 2016
* mirabilos <m#mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
* is granted to deal in this work without restriction, including un-
* limited rights to use, publicly perform, distribute, sell, modify,
* merge, give away, or sublicence.
*
* This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
* the utmost extent permitted by applicable law, neither express nor
* implied; without malicious intent or gross negligence. In no event
* may a licensor, author or contributor be held liable for indirect,
* direct, other damage, loss, or other issues arising in any way out
* of dealing in the work, even if advised of the possibility of such
* damage or existence of a defect, except proven that it results out
* of said person's immediate fault when using the work as intended.
*-
* Syntax:
* php ttf2png [text [size [/path/to/font.ttf]]] >out.png
*/
if (!function_exists('gd_info'))
die("Install php5-gd first.");
$gd = gd_info();
if ($gd["FreeType Support"] == false)
die("Compile php5-gd with FreeType 2 support.");
$font = "/usr/src/www/files/FNT/GenI102.ttf";
$fontsize = 30;
$text = "EINVAL";
if (isset($argv[1]))
$text = $argv[1];
if (isset($argv[2]))
$fontsize = $argv[2];
if (isset($argv[3]))
$font = $argv[3];
// Get bounding box
$bbox = imageftbbox($fontsize, 0, $font, $text);
// Transform coordinates into width+height and position
$ascender = abs($bbox[7]);
$descender = abs($bbox[1]);
$size_w = abs($bbox[0]) + abs($bbox[2]);
$size_h = $ascender + $descender;
$x = -$bbox[0];
$y = $ascender;
// Create image
$im = imagecreatetruecolor($size_w, $size_h);
// Allocate colours
$bgcol = imagecolorallocate($im, 0x24, 0x24, 0x24);
$fgcol = imagecolorallocate($im, 0xFF, 0xFF, 0xFF);
// Fill image with background colour
imagefilledrectangle($im, 0, 0, $size_w - 1, $size_h - 1, $bgcol);
// Render text into image
imagefttext($im, $fontsize, 0, $x, $y, $fgcol, $font, $text);
// Convert true colour image (needed for above) to palette image
imagetruecolortopalette($im, FALSE, 256);
// Output created image
imagepng($im, NULL, 9);
exit(0);
If you write multiple strings on the same line and need to calculate the total height and offset of the line, it’s the maximum of all ascenders plus the maximum of all descenders, and $y for all imagefttext calls for that line is similarily the maximum of all ascenders.
Given the result of a call to imagettfbbox(), what is the correct, pixel-perfect point to provide to imagettftext() such that the text will not extend beyond its bounding box?
I am determining the width/height and x/y of the baseline from the bounding box like this:
$box = imagettfbbox($size, $angle, $font, $text);
$boxXCoords = array($box[0], $box[2], $box[4], $box[6]);
$boxYCoords = array($box[1], $box[3], $box[5], $box[7]);
$boxWidth = max($boxXCoords) - min($boxXCoords);
$boxHeight = max($boxYCoords) - min($boxYCoords);
$boxBaseX = abs(min($boxXCoords));
$boxBaseY = abs(min($boxYCoords));
I then draw a filled rectangle on my image of the dimensions of the bounding box:
imagefilledrectangle($image, 0, 0, $boxWidth - 1, $boxHeight - 1, $color);
After that, I draw the text:
imagettftext($image, $size, $angle, $boxBaseX, $boxBaseY, $color, $font, $text);
However, this causes the text to extend beyond the rectangle by a pixel or two. I have seen several attempts to fix this issue on PHP's imagettfbbox() documentation, but they all just suggest substracting a pixel or two here and there, which seems like a hack to me. What's happening here, and why should we need to fudge the numbers to get things right?
I believe there is no perfect way to place text with single-pixel precision on an image based on what imagettfbbox() returns and also using .ttf non-monospaced fonts. Over at the PHP manual many users have posted ways to accomplish this (with and without fudging the numbers); I recommend using jodybrabec's simple function over at the PHP manual, which calculates the exact bounding box. I have tested this one and only in extreme cases is the text positioned at most 1 pixel off in one direction. Nonetheless, if you add some padding (even if it is just 2 or 3 pixels) to your image your text will be within the dimensions of the image 100% of the time.
What happens when you don't subtract one from each of the dimensions in this line:
imagefilledrectangle($image, 0, 0, $boxWidth - 1, $boxHeight - 1, $color);
and instead do this:
imagefilledrectangle($image, 0, 0, $boxWidth, $boxHeight, $color);
The SlightlyMagic HQ Card Generator project renders cards for the strategy card game Magic: the Gathering. The generator is powered by PHP with an advanced text rendering engine built in. I don't know about logic behind the calculations, but the renderer is dead accurate for the purposes of this application. Here's the function that calculates proper bounding boxes (HQ Card Generator 8.x/scripts/classes/font.php):
private function convertBoundingBox ($bbox) {
// Transform the results of imagettfbbox into usable (and correct!) values.
if ($bbox[0] >= -1)
$xOffset = -abs($bbox[0] + 1);
else
$xOffset = abs($bbox[0] + 2);
$width = abs($bbox[2] - $bbox[0]);
if ($bbox[0] < -1) $width = abs($bbox[2]) + abs($bbox[0]) - 1;
$yOffset = abs($bbox[5] + 1);
if ($bbox[5] >= -1) $yOffset = -$yOffset;
$height = abs($bbox[7]) - abs($bbox[1]);
if ($bbox[3] > 0) $height = abs($bbox[7] - $bbox[1]) - 1;
return array(
'width' => $width,
'height' => $height,
'xOffset' => $xOffset, // Using xCoord + xOffset with imagettftext puts the left most pixel of the text at xCoord.
'yOffset' => $yOffset, // Using yCoord + yOffset with imagettftext puts the top most pixel of the text at yCoord.
'belowBasepoint' => max(0, $bbox[1])
);
}
I know this is a little late but imagettfbbox is in points not pixels.
pixel font size in imagettftext instead of point size
Here is my site. I'm trying to make its own testing URL for the moment:
http://boats.instantonlinesuccess.co.uk/boat-names.html
If you look at it you can see what I am trying to do. I'm trying to link the JavaScript to my PHP, so the writing appears with the effects / effect colours / colours. It's all generated via a PHP script which I'm trying to write.
Here is my PHP:
<?php header("Content-type: image/png");
$string = $_GET['text'];
$currentFont =$_GET['font'];
$currentTextColour = $_GET['colour'];
$currentEffect = $_GET['effect'];
$currentEffectColour = $_GET['effectcolour'];
$im = imagecreatefrompng("images/test.png");
$px = (imagesx($im) - 7.5 * strlen($string)) / 2;
//if you exactly know the RGB color indexes//
$rgb = imagecolorexact($im, 0, 0, 0);
//or keep the rgb color by position so at top 0 left 0
$rgb = imagecolorat($im, 0, 0);
imagecolortransparent($im, $rgb);
imagestring($im, 64, $px, 16, $string, $currentTextColour);
imagepng($im); imagedestroy($im);
?>
If you need the JavaScript, then let me know.
You could try changing your first line to this to debug the issue:
header('Content-Type: text/plain');
Lets say I have elements (E1, E2, ... ,En) that have individual values which varies in [1; 100]. I have a circle (see figure below) and each two circle represent range of data.
The problem is how to show distribution of these elements ofEin this circle. Figure below depicts distribution of some elements ofEin the circle, where for example E1=10, E2=35,...,E6=100, E7=91. Are there any ready libraries in PHP or any plugins in jQuery or any ready solution?
I need to implement this problem in my web application using HTML+CSS+jQuery (don't offer solution with flash technologies, please).
Note: It is like creating charts in MS Excel. For example in MS Excel there is a chart type called Radar which more or less implements this problem, but in my case I have circles instead of polygon and I have only limited range of [1;100].
Edit
I have forgotten to mention that in this figure object element which is in the center is the object based on which we are showing distribution. If element matches object with more percentage so close it to the object and vice versa.
This is incomplete, see the GD manual on how to add text, etc.
<?php
$size = 501; //odd
$img = imagecreatetruecolor($size, $size);
$center = array((int) ($size/2), (int) ($size/2));
$colors['white'] = imagecolorallocate($img, 255, 255, 255);
$colors['black'] = imagecolorallocate($img, 0, 0, 0);
$colors['blue'] = imagecolorallocate($img, 0, 0, 255);
imagefill($img, 0, 0, $colors['white']);
$valid_rad = array((int) (($center[0] - 1) * .2), (int) (($center[0] - 1) * .9));
function radius_for($percentage, $valid_rad) {
return $valid_rad[1] - ($valid_rad[1] - $valid_rad[0]) *
($percentage/100);
}
foreach (array(0,25,50,75,100) as $perc) {
$radius = radius_for($perc, $valid_rad);
imagearc($img, $center[0], $center[1], $radius*2, $radius*2,
0, 360, $colors['black']);
}
foreach (array(100,85,70,36,23,2) as $perc) {
$radius = radius_for($perc, $valid_rad);
$angle = pi() * 1.25 + ((rand(0,100) - 50) / 50) * .5;
$x = (int) ($radius * cos($angle)) + $center[0];
$y = (int) ($radius * sin($angle)) + $center[1];
imagefilledellipse($img, $x, $y , 20, 20, $colors['blue']);
}
header("Content-type: image/png");
imagepng($img);
This gives a picture like this:
I don't know of any pre-made graphs like that. However you may be able to replicate it with a scatter plot with a custom background. Here are some jquery ones. You'd have to do the math to create the x,y coordinates with the appropriate distance from the center point but the chart plug-in should take care of the rest.