Write Text/Values On 360 degree Circle - php

I am working on a wheel chart design, I need help in positioning the text on the 360 wheel.
The wheel have 12 sections, each one of 30 degree. The text's offset from the circle outer line should be equal (or similar at least). like in the image below I have mocked up what I need in final result.
So far, What I have tried is splitting each section into separate variable e.g.
$section1_startX = 50;
$section1_endX = 70;
$section1_startY = 310;
$section1_endY = 480;
and then to place text
imagettftext($im, 15, 0, $section1_startX, $section1_startY, $black, $font, "05");
but this is to find/calculate pixels of each line I need to place.
I am sure there is better, dynamic and smart way to put the text at x,y positions based on its values in 360 circle.
can you please help me regarding?

Hi I think you want to find a Point on a given circle with a given degree. Here is a function for calculating point on a circle. I think you can convert this to any other language easily.
public static PointF PointOnCircle(float radius, float angleInDegrees, Point origin)
{
//radius -> Radius of Circle & Origin -> Circle Centre.
// Convert from degrees to radians via multiplication by PI/180
float x = (float)(radius * Math.Cos(angleInDegrees * Math.PI / 180F)) + origin.X;
float y = (float)(radius * Math.Sin(angleInDegrees * Math.PI / 180F)) + origin.Y;
return new PointF(x, y);
}

Related

Convert Latitude/Longitude to Web Mercator Pixels using PHP

I'm trying to convert WGS 84 coordinates into pixels using web mercator projection. I want to print the position on a given map. I came across a PHP function which works fine if the map is an image of the whole world. I've been trying hard to extend this function so it also works if the map is just a clip (e.g. North America). I already found a solution for calculating the width but I failed to adjust the height calculation. How do I have to change the function?
($top is the top left corner of the map image [Array], $bottom the bottom right corner / $this->width is map width, $this->height is map height)
function cor($lat = null, $lon = null, $top, $bottom) {
// Width
$width = ($lon-$bottom[1]) * $this->width/($top[1]-$bottom[1]);
// Height
$latrad = ($lat * M_PI) / 180;
$mercN = log(tan((M_PI / 4) + ($latrad / 2)));
$height = (($this->height / 2) - ($this->width * $mercN / (2 * M_PI)));
return Array($width, $height);
}
Thanks very much in advance! :)

CSS or PHP? color that is 80% of original but without "transparency"?

this might be a tough question.
I have a php function that returns a color value in rgba() with an argument $alpha.
function colorWheel($alpha) {
"rgba(170, 135, 178, ".$alpha.")"
…
}
So when calling …
.title { color: <?php echo colorWheel(.8); ?>; }
… I get rgba(170, 135, 178, .8);
The problem I have with this is that the color is "transparent" and shows "overlays".
However what I really like to have is just 80% of the color value!
Without any transparent overlays.
The question is now how to solve this?
Any creative ideas how to do that? I don't need to use rgba() it's just the easiest thing that came to my mind. Is there a CSS way not to blend overlaying shapes that have an alpha value?
Or is there a php solution to calculate a the 80% version of rgb(170, 135, 178)?
It is important that this calculation works dynamically with the function because there are more colors in the function - this is a follow-up question to "How to return a color-value based a date and random?"!
Thank you in advance.
The Question is what your definition of "80% of the color" actually is.
CSS has 2 color spaces available at the moment: RGB and HSL (which is actually supported pretty well).
You could do the following RGB calculation:
function colorWheel($alpha) {
'rgba('.$r*$alpha.','.$g*$alpha.','.$b*$alpha.', 1)';
…
}
Or you could take HSL and just reduce the luminance (and or Saturation) channel by 20%. The HSL colorspace is more intuitive when doing things like making colors darker/brighter.
function colorWheel($alpha) {
"hsla($h,$s,".$l*$alpha.",1)";
// or
// ("hsla($h, "+$s*$alpha+", $l, 1)";)
…
}
These all yield (slightly) different results.
The colorspaces can be converted into each other via some not too complicated formulas. Perhaps you should take a look at a random colorpicker(e.g. this one or that one) and then decide, which way of calculation suits you best.
that should do it:
function colorWheel($alpha) {
$r = round(170 * $alpha);
$g = round(135 * $alpha);
$b = round(178 * $alpha);
"rgba($r, $g, $b, 1)";
…
}
well, that makes the color darker, if you want to make it lighter you have to put alpha to a value > 1, and also check if r,g or b goes over 255 and set it to 255 if it does
To simulate color with opacity you also need background color. Lets say, that R,G,B are background color components, and r,g,b are you color components.
If you want to simulate opacity color on specific background, you should take corresponding values of same canal and add them with specific weights:
r = r*alpha + R*(1-alpha)
g = g*alpha + G*(1-alpha)
b = b*alpha + B*(1-alpha)
Lets take simple example. You want to get alpha = 0.8 on color rgb(r,g,b) = rgb(255,0,0) (red) on background rgb(R,G,B) = rgb(255,255,255) (white). That means, you need sum 80% your color + 20% BG:
r = 255*0.8 + 255*0.2 = 255
g = 0*0.8 + 255*0.2 = 51
b = 0*0.8 + 255*0.2 = 51

What is the correct way to determine text coordinates a from bounding box?

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

centre text between 2 coords

I am trying to centre a line of text, of known width along a line specified as start and end coordinates.
The purpose is to write text around a polygon, so the lines are not always horizontal.
Currently I have the following function which takes the start x and y and the finish x and y of a line and the width of the text (in pixels).
The text will be drawn starting at x1, y1 at the correct angle to follow the line.
To centre this text on the line I have tried to calculate left padding in pixels which should be applied to x1, y1 to move the text the correct amount from its left origin.
The following function is my attempt at modifying the coordinates to implement the above concept. But its not quite right. I end up with text slightly off line, sometimes x is out sometimes y, depends on the face but neither x or y is correct.
private function CenterTextOnLine(&$x1, &$y1, &$x2, &$y2, $width)
{
$distance = $this->getDistance($x1, $y1, $x2, $y2);
//calculate the left padding required in pixels
$padding = ($distance - $width) / 2;
//what factor do we need to alter x1, y1 by?
$factor = ($distance / $padding);
$gradient = ($y2-$y1)/($x2-$x1); //gradient to alter y by
$x1 += abs($x2-$x1) / $factor; //move start x by factor
$y1 += ($gradient / $factor); //add factor of gradient to start y
return;
}
If anyone can see my error, or knows an algorithm for this purpose, I would very much appreciate your input.
Thanks for your time.
I think this should work:
$y1 += ($y2-$y1)/$factor;
$k = $how_much_distance_I_want/sqrt(1+$gradient*$gradient);
$x1 += $k*$gradient;
$y1 -= $k;
return;
While you are at it, I think the abs in
$x1 += abs($x2-$x1) / $factor;
can lead to unexpected results in case ($x2-$x1) is negative (maybe better to leave it without abs), and depending on how php deals with infinities, you might want to have a plan for the case that $factor === 0.

Generating gradients from a hexcode or RGB in PHP/jQuery?

I'm trying to figure out how to write a function that programmatically retrieves a background-color css attribute and creates two outputs (one slightly darker than the background-color, one slightly lighter).
The idea is being able to generate a very basic linear gradient from a single color selection.
Anyone have any ideas?
To scale a color correctly, you have to multiply each RGB value by a proportion. E.g., if your color is #00417b and you want a color that is 125% lighter color then you have to do this:
var dark = {r: 0, g: 65, b: 123};
var light = {r: Math.round(Math.min(dark[r]*1.25, 255)),
g: Math.round(Math.min(dark[g]*1.25, 255)),
b: Math.round(Math.min(dark[b]*1.25, 255))};
Compare the result for yourself: dark is #00417b, and light is #00519A, although it's perfectly valid CSS to describe them as rgb(0, 65, 123) and rgb(0, 81, 154) and probably easier too. By scaling colors in this way they will appear to be at the same level of saturation, something that simply adding or subtracting numbers will not achieve.
Be aware that since values are clamped at [0, 255], if you keep shifting colors, then feeding them back into this process, you can destroy information about the proportion of red, green and blue in the source color. For this reason, keep the original color saved and try to use that as your input each time.
Since your question asked specifically about gradients though, this is how you would go between two color values:
// Suppose you have a container which is X pixels high and you want to insert a 1-pixel tall
// element at each pixel, going vertically
var min = Math.min;
var max = Math.max;
var round = Math.round;
function get_color_for_height(startColor, endColor, height, row) {
var scale = row/height;
var r = startColor[red] + scale*(endColor[red] - startColor[red]);
var b = startColor[blue] + scale*(endColor[blue] - startColor[blue]);
var g = startColor[green] + scale*(endColor[green] - startColor[green]);
return {
r: round(min(255, max(0, r))),
g: round(min(255, max(0, g))),
b: round(min(255, max(0, b)))
}
}
// some psuedo-code using an imaginary framework
for(var h = 0; h < height; h++) {
var div = new Element('div');
div.height = 1;
div.backgroundColor = get_color_for_height(start, end, height, h);
container.insert('top', div);
}
To generate a darker or lighter vairant, a simple possibility is just to add or subtract a fixed number to all three componenents, capping it at 0 and 255/0xFF.

Categories