I have the following php code:
<?php
$image = imagecreatefrompng("captcha_background.png");
$imgcolor = imagecolorallocate($image, 0, 0, 0);
imagesetthickness($image, 2);
imageline($image, 0, 25, 40, 90, $imgcolor);
?>
The method "imageline" draws a straight line on my image from the coordinates 0 (x) 25 (y) to 40 (x) 90 (y).
The result is the following image:
What I'm confused about is the reverse of the bottom and the top when using coordinate systems in php.
Normally 0 (The starting point) would be in the lower left corner, but when assigning coordinates in the method "imageline" the 0 (Starting point) is located in the upper left corner?
Expected result:
(The image is 300x100 pixels)
Could someone please explain why this is happening?
This is not a mathematical graph. The typical coordinate system used in development (as far as I know) is to have the first quadrant at the lower right. That is, 0x0 is at the top left. This applies to all html elements that have widths and heights (the elements drop down, they do not fall up).
The motivation appears to be the fact that it's hard to tell how much height you have to work with without knowing the absolute height of the image, which you may not know at any given time, and which may change frequently.
That's how the coordinates are defined in GD, nothing to worry about.
http://www.php.net/manual/en/function.imagedashedline.php :
y1: Upper left y coordinate 0, 0 is the top left corner of the image.
I believe this is the standard for the GD image library as they define the natural origin as the top-left corner.
Related
One thing I've been struggling with in pChart is keeping the Y axis within the boundaries of the compass directions. In my case I am attempting to plot wind direction for a weather station, however the resulting charts always come out with a scale of 0-400. This prevents me from adding meaningful customizations such as defining horizontal bars at 0, 90, 180, and 270 degrees.
Some sample code to generate results...
<?php
include("pchart/class/pData.class.php");
include("pchart/class/pDraw.class.php");
include("pchart/class/pImage.class.php");
$points = array(0, 10, 90, 270, 345, 355);
$scale = array(
'Mode' => SCALE_MODE_MANUAL,
'ManualScale' => array(0=>array("Min"=>0,"Max"=>360)),
);
$data = new pData();
$data->addPoints($points, "dir");
$picture = new pImage(256, 128, $data, TRUE);
$picture->setFontProperties(array("FontName" => "pchart/fonts/GeosansLight.ttf","FontSize" =>10));
$picture->setGraphArea(40,10,216,108);
$picture->drawScale($scale);
$picture->drawPlotChart(array("PlotSize"=>1,"PlotBorder"=>TRUE,"BorderSize"=>1));
$picture->Render("wind.png");
echo "<img src='wind.png'>";
And the resulting chart...
Wind direction chart results
As you can see, the Y axis does not honor the requested ManualScale settings despite the data points being well within the boundaries. I have tried use a Max of 359 and 360 (with 360 being the more correct choice since decimal values may be entered). I have also tried specifying scale MinDivHeight (divided by 5 to get the four compass directions) and/or Factors (testing with multiples of 90), but values are always overwritten in favor of a chart that spans 0-400. On the other hand, using a scale of 0-100 for percentage data works exactly as expected.
Am I missing something, or is this just a limitation of pChart that makes it impossible to perform this simple task?
It turns out I was really close to an answer and didn't realize it. The "Factors" attribute is barely mentioned in the pChart wiki with no information on how to use it, but I had previously run across a discussion of it where the answer was to provide the value in the amount you wanted to split your axis by. That answer was only partially correct -- you must supply an array that also includes the number of times you want to split the axis.
So to answer my own question, I needed to add another line to the $scale array:
'Factors' => array(4,90)
With that line in place, setting Max to either 359 or 360 both resulted in a chart with a scale of 0-360, including horizontal breaks automatically created at 0, 90, 180, 270, and 360. Once I applied this line to my existing code it generated a wind direction plot exactly as desired.
I have 2 images I am placing on top of a 1080X1920 canvas.
One is a rectangle that is 800x400 and it is sitting on the 1080X1920 canvas with top left coordinates of x=140 and y=1200
Then I have another image that is the same size of the canvas 1080X1920, but also has a rectangle on it at the exact same coordinates as the first rectangle. I am overlaying this 1080X1920 image at x=0 and y=0 on the canvas so that the rectangle already in this image lines up perfectly with the rectangle that is already placed on the canvas.
My problem is, I need to apply a rotation to both of these and the black and red rectangles need to match up in positioning AFTER the rotation is applied. Could be any rotation, but let's say it is a 15 degree rotation.
When each element is placed on the canvas and then the 15 degree rotation is applied, the rectangles no longer align because of the difference in image size and the offset in rotation as they both rotate around the center point which looks to be my only option in this case.
So I am hoping to sort out a formula I can use that would rectify the positioning of the 1080X1920 image so that the object already embedded in that image lines up with separately overlaid image.
There are of course other ways to deal with this problem, but right now, they would make things quite a bit more difficult, so I wanted to see if this was possible to calculate first.
I have tried several ways to calculate this, but am not super mathematically proficient, so I am grasping at straws at best.
Oh and because I am not extremely mathematically proficient, any dumbing-down of mathematical terms is appreciated. ;)
Oh and possibly this post answers this question, but I can't wrap my head around whether or not it does, so if someone can let me know if it does, I will try harder to understand and apply it to my particular case.
How to recalculate the coordinates of a point after scaling and rotation?
Any rotation is done around a "center of rotation". You don't tell the centers you use, but they can be:
Center of the canvas.
Center of each image (middle point of its four corners),
Some corner.
Any other point.
If both rotations are not the same, then there's not possible match.
It seems you use the center of each image. Then, to match the second rectangle to the first one, after you rotate the first image you must do in this order:
Translate the second image so its center of rotation is exactly the
same as the center of rotation of the first image. The vector of translation is the coordinates difference for X,Y centers.
Rotate the second image with the same angle as the first image rotation.
This boils down to tracking where the original (0,0) points are on the two images after rotation.
Let's define the problem a bit cleaner:
red.png: 800x400
black.png: 1080X1920
rotate both by 15° (or θ = 15 * π/180) with rotate filter (assuming the actual values are within -90° and 90°)
how to place a rotated red.png on rotated black.png at the ORIGINAL top-left coordinates (x=140,y=120)
Consider 2 FFmpeg rotation commands:
ffmpeg red.png -vf rotate=15*PI/180:ow=hypot(iw\\,ih):oh=ow -frames:v 1 red_rotated.png
ffmpeg black.png -vf rotate=15*PI/180 -frames:v 1 black_rotated.png
Note that red_rotated.png is enlarged to inscribe the red rectangle while black_rotated.png maintains the same size. Now, the question is "where are the original top-left corner now?"
red_rotated.png:
0 < θ < π/2 cases: (xr,yr) = (h sin(θ), 0)
-π/2 < θ < 0 cases: (xr,yr) = (0, w sin(θ))
black_rotated.png: Same as red_rotated.png but now cropped to the original size
new size: ow = w cos(θ) + h sin(|θ|), oh = w sin(|θ|) + h cos(θ)
size delta: dw = (ow - w)/2, dh = (oh - h)/2
0 < θ < π/2 cases: (xb, yb) = (h sin(-θ) - dw, -dh)
-π/2 < θ < 0 cases: (xb, yb) = (-dw, w sin(θ) - dh)
Now, where is the insertion coordinate (x,y) = (140,120) on black_rotated.png:
rotate wrt the original corner: (x1,y1) = (x cos(θ) - y sin(θ), x sin(θ) + y cos(θ)
shift wrt the new black corner: (x2,y2) = (x1 + xb, y1 + yb)
shift wrt the new red corner: (x3,y3) = (x2 - xr, y2 - yr)
Accordingly overlaying red_rotated.png with the offset (x3,y3) onto black_rotated.png should get you the results you want.
Disclaimer: I have not verified my math, but this is should be a good starting point.
I've got a problem with rotating text on PDF.
My form before any rotation looks like this:
The positions seems to be correct, when I set rotation to 0.
But the rotation may vary from the item and can be anything.
Rotation from the data is, that LEFT has -90 degrees, right +90 degrees and bottom +180 degrees.
When I set it to it, the only correct text is TOP (because rotation is 0). The rest of text items are totally wrong, some even not visible.
I've managed to do the rotation with images, example:
The code I've used for image rotation is:
$pdf->StartTransform();
$pdf->Rotate(
-$item['rotation'],
$imagePosition['x'] + $imageSize['x'] / 2,
$imagePosition['y'] + $imageSize['y'] / 2);
$pdf->Image(
$filename,
$imagePosition['x'],
$imagePosition['y'],
$imageSize['x'],
$imageSize['y'],
strtoupper($imagick->getImageFormat()),
'',
'',
false,
(int)$dpi,
'',
false,
false,
0,
false,
false,
false
);
$pdf->StopTransform();
For image it's easy, because I have position and size.
How to do the propper Text rotation?
I was trying to write that text in Imagick and from there get text width and height and try to do the same like for the image, but without any success.
My code for text rotation:
$pdf->StartTransform();
$pdf->Rotate(
-$item['rotation'],
$fontPosition['x'] - $textSize['x'] / 2,
$fontPosition['y'] - $textSize['y'] / 2
);
list($r, $g, $b) = sscanf($item['attributes']['color'], "#%02x%02x%02x");
$pdf->SetTextColor($r, $g, $b);
$pdf->SetFont($fontName, '', $fontSize, '', false);
$pdf->Text($fontPosition['x'], $fontPosition['y'], $item['value'], 0, 0);
$pdf->StopTransform();
Note that Text size variable is probably not correct.
Any ideas how to write image rotated to PDF without so many complications in TCPDF?
For FPDF I found extension:
http://www.fpdf.org/en/script/script31.php
When I've tried to implement it to TCPDF, then the text was squares, instead of the real content.
PLZ HELP :D
You need to keep track of the current "origin" of the coordinate system, where 0,0 is after the current transformation.
You also need to keep in mind that transformations are combined, and earlier transformations affect later ones.
If you rotate 90 degrees clockwise, then move in +Y (up), you actually move in +X (right)... because the entire coordinate system has rotated.
If on the other hand, you moved (translated), then rotated, your new origin around which you would rotate would be wherever it was you wanted +Y.
So if you do your transformations in the correct order, and know which direction things are drawn, you can place things wherever you want.
If you rotate an untranslated image 90 degrees clockwise, it's rotated around the origin (0,0) and therefore disappears. On the other hand, if you move the origin to take the image's movement around the origin into account, it'll land right where you wanted.
One of the tricks I used when messing around with PDF transformation features/bugs was\s to expand the page by a couple thousand units in every direction. IIRC, an 8.5"x11" page has a media box of [0 0 612 792] (wow, I actually remembered that correctly: go me). If you set the media box to [-2000 -2000 2612 2792], then you have to work hard to screw up your transformation so badly that you can't see where your text/image/whatever landed (though it can, and has, been done).
Note that if your page also has a crop box, you have to mess with it too.
Ok, I've sort it out. Working code for text rotation should be like this:
$pdf->StartTransform();
$pdf->Rotate(
-$item['rotation'],
$fontPosition['x'] + $textSize['x'] / 2,
$fontPosition['y'] + $textSize['y'] / 2
);
list($r, $g, $b) = sscanf($item['attributes']['color'], "#%02x%02x%02x");
$pdf->SetTextColor($r, $g, $b);
$pdf->SetFont($fontName, '', $fontSize, '', false);
$pdf->Text($fontPosition['x'], $fontPosition['y'], $item['value'], 0, 0);
$pdf->StopTransform();
Wrong was:
$fontPosition['x'] - $textSize['x'] / 2,
$fontPosition['y'] - $textSize['y'] / 2
Should be:
$fontPosition['x'] + $textSize['x'] / 2,
$fontPosition['y'] + $textSize['y'] / 2
I've tried this combination before, but my textSize wasn't correct (I was calculating it with imagick). In my case, I've counted percentage of our draw area that text is taking and after that I correctly counted it in mm.
Hope it helps someone else.
What is the application of a bounding box of a text in PHP imageftbbox?
In the example of the manual, it was used to determine the coordinates for the imagefttext function. Is it necessary? Maybe I am missing something here.
...
// First we create our bounding box
$bbox = imageftbbox(10, 0, $font, 'The PHP Documentation Group');
// This is our cordinates for X and Y
$x = $bbox[0] + (imagesx($im) / 2) - ($bbox[4] / 2) - 5;
$y = $bbox[1] + (imagesy($im) / 2) - ($bbox[5] / 2) - 5;
imagefttext($im, 10, 0, $x, $y, $black, $font, 'The PHP Documentation Group');
...
Flylib has extensive information about imagettfbox().
Here's some relevant information from the linked information:
(image copyright Flylib)
From image above, you can see that there are 4 point with 8 information (as the documentation already stated):
Array information Bounding box coordinate
===================================================================
0 X coordinate of the lower left hand corner
1 Y coordinate of the lower left hand corner
2 X coordinate of the lower right hand corner
3 Y coordinate of the lower right hand corner
4 X coordinate of the upper right hand corner
5 Y coordinate of the upper right hand corner
6 X coordinate of the upper left hand corner
7 Y coordinate of the upper left hand corner
Again, as stated by the documentation, this information is relative to text regardless of the angle. Therefore, if you rotate the text to 90 degree clockwise, the bounding box become:
Array information Bounding box coordinate
===================================================================
0 X coordinate of the upper left hand corner
1 Y coordinate of the upper left hand corner
2 X coordinate of the lower left hand corner
3 Y coordinate of the lower left hand corner
4 X coordinate of the lower right hand corner
5 Y coordinate of the lower right hand corner
6 X coordinate of the upper right hand corner
7 Y coordinate of the upper right hand corner
Another resources that I believe will help you better grasp the basic idea around bounding box:
http://ruquay.com/sandbox/imagettf/
the pair of functions imageftbbox and imagettfbbox allow you to know how much space will your text take on an image (the first one is used for free type text and the second for true type text)
this way, if you have an application that generates some images and writes on them variable text (user input or something like this) you can decide how/where to place that text using functions like imagefttext or imagettftext (same difference - the font)
so you can do something like:
$bbox = imagettfbbox(34, 0, 'myriadb.otf', strtoupper($name)); //font size 34, text in a horizontal line, use myriadb.otf as font, the user name as variable text
$text_width = $bbox[2]; // this is how much space the name will take
$margin = (CARD_WIDTH-($text_width))/2; // a constant knowing the width of the resulting card.. this way we will center the name..
imagettftext($image, 34, 0, $margin, $y, $white, 'myriadb.otf', strtoupper($name));// $image resource, 34-same size, same angle, the margin is the the x coordinate, fixed $y, color, font and the same text
I have run into some trouble with the gd library's imagefilledpolygon().
For some reason some of my lines were ending up 1px out of place so I decided to debug it using imagepixelset to set the colour of my shapes points to red.
alt text http://www.degreeshowcase.com/other/1.gif
if you look at the picture you can see some of the points are inside the shape ... some are outside....its very illogical.
(the picture has been scaled up to make it more visible)
Does anyone have a solution?
Update:
My points for the shape above were: 0,0 40,0 40,20 20,20 20,40 0,40
I require that the height and width of the shape produced should be a multiple of 20.... but for some reason some of the shape ends up 21 px high or wide.
I have made a script to work out what the points would be to get the shape I wanted but I can not work out why and so I can't work out a script to correct all my shapes.
<?php
// set up array of points for polygon
$values = array(0,0, 39,0, 39,20, 19,20, 19,39, 0,39);
//My original values were 0,0 40,0 40,20 20,20 20,40 0,40
//I do not understand why some values require minus 1 and others can remain as they were (a multiple of 20)
// create image
$image = imagecreatetruecolor(40, 40);
// allocate colors
$bg = imagecolorallocate($image, 200, 200, 200);
$blue = imagecolorallocate($image, 0, 0, 255);
// fill the background
imagefilledrectangle($image, 0, 0, 39, 39, $bg);
// draw a polygon
imagefilledpolygon($image, $values, 6, $blue);
// flush image
header('Content-type: image/png');
imagepng($image);
imagedestroy($image);
?>
My guess is that you're mixing up width with position.
For example a line from 0px to 9px is 10px long... if you used length as the second parameter instead of position, it would end up 11px long.
If I could see some code I could confirm this.
Normal polygon rendering ensures that each pixel can only be in one polygon, if the 2 polygons share an edge. If you imagine drawing 2 squares, next to each other, so they share a common edge, you don't want to render the pixels along that edge twice.
There is an explanation of determining which pixels on the edge of a polygon should be considered inside the polygon here: http://www.gameprogrammer.com/5-poly.html
A common solution is to say that "pixels on the left and top edges of a polygon belong to the polygon and pixels on the right and bottom edges don't". I am not 100% sure what solution GD uses, as I could not find any documentation on it, but I expect it is something similar to this.
I spoke to the guy who currently develops the GD library he explained that it follows the 'Winding number algorithm' - can be found here. Having looked at my example image, it does match how the 'winding number algorithm' works, however the function should take this into account and produce the shape that was input.
As far as I can see the only way to accurately (to the pixel) generate a concave polygon with this function is to write another function that also applies the winding rule to your coordinates and adjusts them accordingly and then put it into the function.