I am working on a project that uses the Juce library to display graphics.
So far I have been using the library’s API functions to generate linear and radial gradients, however that’s the only two types of gradients that this library supports. I am now in need of generating a different type of gradient, one that follows the shape of a regular convex polygon. The key word here is REGULAR, meaning a polygon with all edges of the same length and with all vertices lying on a single circle.
For the case of a pentagon, here is a picture to better show the result I would like to get:
http://www.filterforge.com/wiki/index.php/Polygonal_Gradient
For my application, I want to be able to specify a polygonal gradient with any number of edges. (pentagon, hexagon, octagon, etc…)
Given the limitations of the API, the only way I can produce the desired result is to fill a surface matrix pixel by pixel, mathematically calculating the values of the R, G, B, A components for each pixel.
Here is the code I have so far:
void render_surface(unsigned char *surface_data,
int width, int height, int linestride,
int num_vertices, t_rgba *color1, t_rgba *color2)
{
const double center_x = 0.5 * width;
const double center_y = 0.5 * height;
const double radius = 0.5 * MIN(width, height);
int x, y;
for (y = height; --y >= 0;) {
uint32_t *line = (uint32_t *)data;
data += linestride;
const double dy = y - center_y;
for (x = width; --x >= 0;) {
const double dx = x - center_x;
double rho = hypot(dx, dy);
rho /= radius; // normalize radius
// constrain
rho = CLIP(rho, 0.0, 1.0);
// interpolate
double a = color2->alpha + (color1->alpha - color2->alpha) * rho;
double r = color2->red + (color1->red - color2->red ) * rho;
double g = color2->green + (color1->green - color2->green) * rho;
double b = color2->blue + (color1->blue - color2->blue ) * rho;
// premultiply alpha
r *= a;
g *= a;
b *= a;
#if LITTLE_ENDIAN
*line++ = ((unsigned int)((256.0 * (1.0 - DBL_EPSILON)) * a) << 24) // alpha
| ((unsigned int)((256.0 * (1.0 - DBL_EPSILON)) * r) << 16) // red
| ((unsigned int)((256.0 * (1.0 - DBL_EPSILON)) * g) << 8) // green
| (unsigned int)((256.0 * (1.0 - DBL_EPSILON)) * b); // blue
#else
*line++ = ((unsigned int)((256.0 * (1.0 - DBL_EPSILON)) * b) << 24) // blue
| ((unsigned int)((256.0 * (1.0 - DBL_EPSILON)) * g) << 16) // green
| ((unsigned int)((256.0 * (1.0 - DBL_EPSILON)) * r) << 8) // red
| (unsigned int)((256.0 * (1.0 - DBL_EPSILON)) * a); // alpha
#endif
}
}
}
The above code produces a radial gradient, the same type of gradient I could produce using one API function. However it seems to be a good starting point to tackle the problem.
surface_data – is a matrix of 8 bit values representing the pixel intensities of the Red, Green, Blue and Alpha components.
num_vertices – is the number of vertices (equally spaced on a single circle) that we want our polygonal gradient to have.
color1 – starting color of the gradient.
color2 – ending color of the gradient.
I would like to know how I can fill a surface in the same fashion, creating a polygonal gradient as opposed to radial.
Thanks for any help.
- Luigi
Rethinking about this problem a little bit…
if we consider the origin of our coordinate system the center of the polygon, it boils down to finding an equation such that for any input point in cartesian coordinates, the output is the distance from the closest side of the polygon.
My intuition tells me that there must be some kind of closed form solution because:
for a circle,
rho = sqrt(dx*dx + dy*dy);
gives us the radial distance from the center of the circle, which could be considered as a polygon with infinite sides.
For a square,
fmax(fabs(dx), fabs(dy));
gives us the Chebyshev distance from the closest side of the square, which could be considered as a polygon with 4 sides.
So, I am thinking that some kind of combination of the two formulas should give the intermediary cases, which would solve the initial problem.
Am I totally off thinking along these lines?
- Luigi
This is roughly how I’d approach it …
‘O’ & ‘P’ be ‘Line1’ and
containing polygon segment be ‘Line2’
Now the color fraction at P is defined by the distance of P to the origin relative to the distance of IP to the origin.
Edit: I’ve implemented the algorithm above and this is the output …
Edit2:
Here’s the (Delphi) code