Plot ellipse from rectangle - algorithm

I have been looking all over the Web for a way to plot an ellipse from rectangle coordinates, that is, top-left corner (x, y) and size (width and height). The only ones I can find everywhere are based on the Midpoint/Bresenham algorithm and I can't use that because when working with integer pixels, I lose precisions because these algorithms use a center point and radials.
The ellipse MUST be limited to the rectangle's coordinates, so if I feed it a rectangle where the width and height are 4 (or any even number), I should get an ellipse that completely fits in a 4x4 rectangle, and not one that will be 5x5 (like what those algorithms are giving me).
Does anyone know of any way to accomplish this?
Thanks!

Can you not get the width and height (divided by 2) and center of the rectangle then plug that into any ellipse drawing routine as its major, minor axis and center? I guess I'm not seeing the problem all the way here.

I had the same need. Here is my solution with code. The error is at most half a pixel.
I based my solution on the McIlroy ellipse algorithm, an integer-only algorithm which McIlroy mathematically proved to be accurate to a half-pixel, without missing or extra points, and correctly drawing degenerate cases such as lines and circles. L. Patrick further analyzed McIlroy's algorithm, including ways to optimize it and how a filled ellipse can be broken up into rectangles.
McIlroy's algorithm traces a path through one quadrant of the ellipse; the remaining quadrants are rendered through symmetry. Each step in the path requires three comparisons. Many of the other ellipse algorithms use octants instead, which require only two comparisons per step. However, octant-based methods have are notoriously inaccurate at the octant boundaries. The slight savings of one comparison is not worth the inaccuracy of the octant methods.
Like virtually every other integer ellipse algorithm, McIlroy's wants the center at integer coordinates, and the lengths of the axes a and b to be integers as well. However, we want to be able to draw an ellipse with a bounding box using any integer coordinates. A bounding box with an even width or even height will have a center on an integer-and-a-half coordinate, and a or b will be an integer-and-a-half.
My solution was to perform calculations using integers that are double of what is needed. Any variable starting with q is calculated from double pixel values. An even q variable is on an integer coordinate, and an odd q variable is at an integer-and-a-half coordinate. I then re-worked McIroy's math to get the correct mathematical expressions with these new doubled values. This includes modifying starting values when the bounding box has even width or height.
Behold, the subroutine/method drawEllipse given below. You provide it with the integer coordinates (x0,y0) and (x1,y1) of the bounding box. It doesn't care if x0 < x1 versus x0 > x1; it will swap them as needed. If you provide x0 == x1, your will get a vertical line. Similarly for the y0 and y1 coordinates. You also provide the boolean fill parameter, which draws only the ellipse outline if false, and draws a filled ellipse if true. You also have to provide the subroutines drawPoint(x, y) which draws a single point and drawRow(xleft, xright, y) which draws a horizontal line from xleft to xright inclusively.
McIlroy and Patrick optimize their code to fold constants, reuse common subexpressions, etc. For clarity, I didn't do that. Most compilers do this automatically today anyway.
void drawEllipse(int x0, int y0, int x1, int y1, boolean fill)
{
int xb, yb, xc, yc;
// Calculate height
yb = yc = (y0 + y1) / 2;
int qb = (y0 < y1) ? (y1 - y0) : (y0 - y1);
int qy = qb;
int dy = qb / 2;
if (qb % 2 != 0)
// Bounding box has even pixel height
yc++;
// Calculate width
xb = xc = (x0 + x1) / 2;
int qa = (x0 < x1) ? (x1 - x0) : (x0 - x1);
int qx = qa % 2;
int dx = 0;
long qt = (long)qa*qa + (long)qb*qb -2L*qa*qa*qb;
if (qx != 0) {
// Bounding box has even pixel width
xc++;
qt += 3L*qb*qb;
}
// Start at (dx, dy) = (0, b) and iterate until (a, 0) is reached
while (qy >= 0 && qx <= qa) {
// Draw the new points
if (!fill) {
drawPoint(xb-dx, yb-dy);
if (dx != 0 || xb != xc) {
drawPoint(xc+dx, yb-dy);
if (dy != 0 || yb != yc)
drawPoint(xc+dx, yc+dy);
}
if (dy != 0 || yb != yc)
drawPoint(xb-dx, yc+dy);
}
// If a (+1, 0) step stays inside the ellipse, do it
if (qt + 2L*qb*qb*qx + 3L*qb*qb <= 0L ||
qt + 2L*qa*qa*qy - (long)qa*qa <= 0L) {
qt += 8L*qb*qb + 4L*qb*qb*qx;
dx++;
qx += 2;
// If a (0, -1) step stays outside the ellipse, do it
} else if (qt - 2L*qa*qa*qy + 3L*qa*qa > 0L) {
if (fill) {
drawRow(xb-dx, xc+dx, yc+dy);
if (dy != 0 || yb != yc)
drawRow(xb-dx, xc+dx, yb-dy);
}
qt += 8L*qa*qa - 4L*qa*qa*qy;
dy--;
qy -= 2;
// Else step (+1, -1)
} else {
if (fill) {
drawRow(xb-dx, xc+dx, yc+dy);
if (dy != 0 || yb != yc)
drawRow(xb-dx, xc+dx, yb-dy);
}
qt += 8L*qb*qb + 4L*qb*qb*qx + 8L*qa*qa - 4L*qa*qa*qy;
dx++;
qx += 2;
dy--;
qy -= 2;
}
} // End of while loop
return;
}
The image above shows the output for all bounding boxes up to size 10x10. I also ran the algorithm for all ellipses up to size 100x100. This produced 384614 points in the first quadrant. The error between where each of these points were plotted and where the actual ellipse occurs was calculated. The maximum error was 0.500000 (half a pixel) and the average error among all of the points was 0.216597.

The solution I found to this problem was to draw the closest smaller ellipse with odd dimensions, but pulled apart by a pixel along the even length dimension, repeating the middle pixels.
This can be done easily by using different middle points for the quadrants when plotting each pixel:
DrawPixel(midX_high + x, midY_high + y);
DrawPixel(midX_low - x, midY_high + y);
DrawPixel(midX_high + x, midY_low - y);
DrawPixel(midX_low - x, midY_low - y);
The high values are the ceil'ed midpoint, and the low values are the floored midpoint.
An image to illustrate, ellipses with width 15 and 16:

Related

Generating a pixel-based spiral gradient

I have a program that creates pixel-based gradients (meaning it calculates the step in the gradient for each pixel, then calculates the colour at that step, then gives the pixel that colour).
I'd like to implement spiral gradients (such as below).
My program can create conic gradients (as below), where each pixel is assigned a step in the gradient according to the angle between it and the midpoint (effectively mapping the midpoint-pixel angle [0...2PI] to [0...1]).
It would seem to me that a spiral gradient is a conic gradient with some additional function applied to it, where the gradient step for a given pixel depends not only on the angle, but on some additional non-linear function applied to the euclidean distance between the midpoint and pixel.
I envisage that a solution would take the original (x, y) pixel coordinate and displace it by some amounts in the x and y axes resulting in a new coordinate (x2, y2). Then, for each pixel, I'd simply calculate the angle between the midPoint and its new displaced coordinate (x2, y2) and use this angle as the gradient step for that pixel. But it's this displacement function that I need help with... of course, there may be other, better ways.
Below is a simple white-to-black conic gradient. I show how I imagine the displacement would work, but its the specifics about this function (the non-linearity), that I'm unable to implement.
My code for conic gradient:
public void conicGradient(Gradient gradient, PVector midPoint, float angle) {
float rise, run;
double t = 0;
for (int y = 0, x; y < imageHeight; ++y) {
rise = midPoint.y - y;
run = midPoint.x;
for (x = 0; x < imageWidth; ++x) {
t = Functions.fastAtan2(rise, run) + Math.PI - angle;
// Ensure a positive value if angle is negative.
t = Functions.floorMod(t, PConstants.TWO_PI);
// Divide by TWO_PI to get value in range 0...1
step = t *= INV_TWO_PI;
pixels[imageWidth * y + x] = gradient.ColorAt(step); // pixels is 1D pixel array
run -= 1;
}
}
}
By eye, it looks like after t = ... fastAtan2..., you just need:
t += PConstants.TWO_PI * Math.sqrt( (rise*rise + run*run) / (imageWidth * imageWidth + imageHeight * imageHeight) )
This just adds the distance from the center to the angle, with appropriate scaling.

Processing - creating circles from current pixels

I'm using processing, and I'm trying to create a circle from the pixels i have on my display.
I managed to pull the pixels on screen and create a growing circle from them.
However i'm looking for something much more sophisticated, I want to make it seem as if the pixels on the display are moving from their current location and forming a turning circle or something like this.
This is what i have for now:
int c = 0;
int radius = 30;
allPixels = removeBlackP();
void draw {
loadPixels();
for (int alpha = 0; alpha < 360; alpha++)
{
float xf = 350 + radius*cos(alpha);
float yf = 350 + radius*sin(alpha);
int x = (int) xf;
int y = (int) yf;
if (radius > 200) {radius =30;break;}
if (c> allPixels.length) {c= 0;}
pixels[y*700 +x] = allPixels[c];
updatePixels();
}
radius++;
c++;
}
the function removeBlackP return an array with all the pixels except for the black ones.
This code works for me. There is an issue that the circle only has the numbers as int so it seems like some pixels inside the circle won't fill, i can live with that. I'm looking for something a bit more complex like I explained.
Thanks!
Fill all pixels of scanlines belonging to the circle. Using this approach, you will paint all places inside the circle. For every line calculate start coordinate (end one is symmetric). Pseudocode:
for y = center_y - radius; y <= center_y + radius; y++
dx = Sqrt(radius * radius - y * y)
for x = center_x - dx; x <= center_x + dx; x++
fill a[y, x]
When you find places for all pixels, you can make correlation between initial pixels places and calculated ones and move them step-by-step.
For example, if initial coordinates relative to center point for k-th pixel are (x0, y0) and final coordinates are (x1,y1), and you want to make M steps, moving pixel by spiral, calculate intermediate coordinates:
calc values once:
r0 = Sqrt(x0*x0 + y0*y0) //Math.Hypot if available
r1 = Sqrt(x1*x1 + y1*y1)
fi0 = Math.Atan2(y0, x0)
fi1 = Math.Atan2(y1, x1)
if fi1 < fi0 then
fi1 = fi1 + 2 * Pi;
for i = 1; i <=M ; i++
x = (r0 + i / M * (r1 - r0)) * Cos(fi0 + i / M * (fi1 - fi0))
y = (r0 + i / M * (r1 - r0)) * Sin(fi0 + i / M * (fi1 - fi0))
shift by center coordinates
The way you go about drawing circles in Processing looks a little convoluted.
The simplest way is to use the ellipse() function, no pixels involved though:
If you do need to draw an ellipse and use pixels, you can make use of PGraphics which is similar to using a separate buffer/"layer" to draw into using Processing drawing commands but it also has pixels[] you can access.
Let's say you want to draw a low-res pixel circle circle, you can create a small PGraphics, disable smoothing, draw the circle, then render the circle at a higher resolution. The only catch is these drawing commands must be placed within beginDraw()/endDraw() calls:
PGraphics buffer;
void setup(){
//disable sketch's aliasing
noSmooth();
buffer = createGraphics(25,25);
buffer.beginDraw();
//disable buffer's aliasing
buffer.noSmooth();
buffer.noFill();
buffer.stroke(255);
buffer.endDraw();
}
void draw(){
background(255);
//draw small circle
float circleSize = map(sin(frameCount * .01),-1.0,1.0,0.0,20.0);
buffer.beginDraw();
buffer.background(0);
buffer.ellipse(buffer.width / 2,buffer.height / 2, circleSize,circleSize);
buffer.endDraw();
//render small circle at higher resolution (blocky - no aliasing)
image(buffer,0,0,width,height);
}
If you want to manually draw a circle using pixels[] you are on the right using the polar to cartesian conversion formula (x = cos(angle) * radius, y = sin(angle) * radius).Even though it's focusing on drawing a radial gradient, you can find an example of drawing a circle(a lot actually) using pixels in this answer

How to use Bresenham's line drawing algorithm with sub pixel bias?

Bresenham's line drawing algorithm is well known and quite simple to implement.
While there are more advanced ways to draw anti-ailesed lines, Im interested in writing a function which draws a single pixel width non anti-aliased line, based on floating point coordinates.
This means while the first and last pixels will remain the same, the pixels drawn between them will have a bias based on the sub-pixel position of both end-points.
In principle this shouldn't be all that complicated, since I assume its possible to use the sub-pixel offsets to calculate an initial error value to use when plotting the line, and all other parts of the algorithm remain the same.
No sub pixel offset:
X###
###X
Assuming the right hand point has a sub-pixel position close to the top, the line could look like this:
With sub pixel offset for example:
X######
X
Is there a tried & true method of drawing a line that takes sub-pixel coordinates into account?
Note:
This seems like a common operation, I've seen OpenGL drivers take this into account for example - using GL_LINE, though from a quick search I didn't find any answers online - maybe used wrong search terms?
At a glance this question looks like it might be a duplicate of: Precise subpixel line drawing algorithm (rasterization algorithm)However that is asking about drawing a wide line, this is asking about offsetting a single pixel line.
If there isn't some standard method, I'll try write this up to post as an answer.
Having just encountered the same challenge, I can confirm that this is possible as you expected.
First, return to the simplest form of the algorithm: (ignore the fractions; they'll disappear later)
x = x0
y = y0
dx = x1 - x0
dy = y1 - y0
error = -0.5
while x < x1:
if error > 0:
y += 1
error -= 1
paint(x, y)
x += 1
error += dy/dx
This means that for integer coordinates, we start half a pixel above the pixel boundary (error = -0.5), and for each pixel we advance in x, we increase the ideal y coordinate (and therefore the current error) by dy/dx.
First let's see what happens if we stop forcing x0, y0, x1 and y1 to be integers: (this will also assume that instead of using pixel centres, the coordinates are relative to the bottom-left of each pixel1, since once you support sub-pixel positions you can simply add half the pixel width to the x and y to return to pixel-centred logic)
x = x0
y = y0
dx = x1 - x0
dy = y1 - y0
error = (0.5 - (x0 % 1)) * dy/dx + (y0 % 1) - 1
while x < x1:
if error > 0:
y += 1
error -= 1
paint(x, y)
x += 1
error += dy/dx
The only change was the initial error calculation. The new value comes from simple trig to calculate the y coordinate when x is at the pixel centre. It's worth noting that you can use the same idea to clip the line's start position to be within some bound, which is another challenge you'll likely face when you want to start optimising things.
Now we just need to convert this into integer-only arithmetic. We'll need some fixed multiplier for the fractional inputs (scale), and the divisions can be handled by multiplying them out, just as the standard algorithm does.
# assumes x0, y0, x1 and y1 are pre-multiplied by scale
x = x0
y = y0
dx = x1 - x0
dy = y1 - y0
error = (scale - 2 * (x0 % scale)) * dy + 2 * (y0 % scale) * dx - 2 * dx * scale
while x < x1:
if error > 0:
y += scale
error -= 2 * dx * scale
paint(x / scale, y / scale)
x += scale
error += 2 * dy * scale
Note that x, y, dx and dy keep the same scaling factor as the input variables (scale), whereas error has a more complex scaling factor: 2 * dx * scale. This allows it to absorb the division and fraction in its original formulation, but means we need to apply the same scale everywhere we use it.
Obviously there's a lot of room to optimise here, but that's the basic algorithm. If we assume scale is a power-of-two (2^n), we can start to make things a little more efficient:
dx = x1 - x0
dy = y1 - y0
mask = (1 << n) - 1
error = (2 * (y0 & mask) - (2 << n)) * dx - (2 * (x0 & mask) - (1 << n)) * dy
x = x0 >> n
y = y0 >> n
while x < (x1 >> n):
if error > 0:
y += 1
error -= 2 * dx << n
paint(x, y)
x += 1
error += 2 * dy << n
As with the original, this only works in the (x >= y, x > 0, y >= 0) octant. The usual rules apply for extending it to all cases, but note that there are a few extra gotchyas due to the coordinates no-longer being centred in the pixel (i.e. reflections become more complex).
You'll also need to watch out for integer overflows: error has twice the precision of the input variables, and a range of up to twice the length of the line. Plan your inputs, precision, and variable types accordingly!
1: Coordinates are relative to the corner which is closest to 0,0. For an OpenGL-style coordinate system that's the bottom left, but it could be the top-left depending on your particular scenario.
I had a similar problem, with the addition of needing sub-pixel endpoints, I also needed to make sure all pixels which intersect the line are drawn.
I'm not sure that my solution will be helpful to OP, both because its been 4+ years, and because of the sentence "This means while the first and last pixels will remain the same..." For me, that is actually a problem (More on that later). Hopefully this may be helpful to others.
I don't know if this can be considered to be Bresenham's algorithm, but it is awful similar. I'll explain it for the (+,+) quadrant. Lets say you wish to draw a line from point (Px,Py) to (Qx,Qy) over a grid of pixels with width W. Having a grid width W > 1 allows for sub-pixel endpoints.
For a line going in the (+,+) quadrant, the starting point is easy to calculate, just take the floor of (Px,Py). As you will see later, this only works if Qx >= Px & Qy >= Py.
Now you need to find which pixel to go to next. There are 3 possibilities: (x+1,y), (x,y+1), & (x+1,y+1). To make this decision, I use the 2D cross product defined as:
If this value is negative, vector b is right/clockwise of vector a.
If this value is positive, vector b is left/anti-clockwise of vector a.
If this value is zero vector b points in the same direction as vector a.
To make the decision on which pixel is next, compare the cross product between the line P-Q [red in image below] and a line between the point P and the top-right pixel (x+1,y+1) [blue in image below].
The vector between P & the top-right pixel can be calculated as:
So, we will use the value from the 2D cross product:
If this value is negative, the next pixel will be (x,y+1).
If this value is positive, the next pixel will be (x+1,y).
If this value is exactly zero, the next pixel will be (x+1,y+1).
That works fine for the starting pixel, but the rest of the pixels will not have a point that lies inside them. Luckily, after the initial point, you don't need a point to be inside the pixel for the blue vector. You can keep extending it like so:
The blue vector starts at the starting point of the line, and is updated to the (x+1,y+1) for every pixel. The rule for which pixel to take is the same. As you can see, the red vector is right of the blue vector. So, the next pixel will be the one right of the green pixel.
The value for the cross product needs updated for every pixel, depending on which pixel you took.
Add dx if the next pixel was (x+1), add dy if the pixel was (y+1). Add both if the pixel went to (x+1,y+1).
This process is repeated until it reaches the ending pixel, (Qx / W, Qy / W).
All combined this leads to the following code:
int dx = x2 - x2;
int dy = y2 - y1;
int local_x = x1 % width;
int local_y = y1 % width;
int cross_product = dx*(width-local_y) - dy*(width-local_x);
int dx_cross = -dy*width;
int dy_cross = dx*width;
int x = x1 / width;
int y = y1 / width;
int end_x = x2 / width;
int end_y = y2 / width;
while (x != end_x || y != end_y) {
SetPixel(x,y,color);
int old_cross = cross_product;
if (old_cross >= 0) {
x++;
cross_product += dx_cross;
}
if (old_cross <= 0) {
y++;
cross_product += dy_cross;
}
}
Making it work for all quadrants is a matter of reversing the local coordinates and some absolute values. Heres the code which works for all quadrants:
int dx = x2 - x1;
int dy = y2 - y1;
int dx_x = (dx >= 0) ? 1 : -1;
int dy_y = (dy >= 0) ? 1 : -1;
int local_x = x1 % square_width;
int local_y = y1 % square_width;
int x_dist = (dx >= 0) ? (square_width - local_x) : (local_x);
int y_dist = (dy >= 0) ? (square_width - local_y) : (local_y);
int cross_product = abs(dx) * abs(y_dist) - abs(dy) * abs(x_dist);
dx_cross = -abs(dy) * square_width;
dy_cross = abs(dx) * square_width;
int x = x1 / square_width;
int y = y1 / square_width;
int end_x = x2 / square_width;
int end_y = y2 / square_width;
while (x != end_x || y != end_y) {
SetPixel(x,y,color);
int old_cross = cross_product;
if (old_cross >= 0) {
x += dx_x;
cross_product += dx_cross;
}
if (old_cross <= 0) {
y += dy_y;
cross_product += dy_cross;
}
}
However there is a problem! This code will not stop in some cases. To understand why, you need to really look into exactly what conditions count as the intersection between a line and a pixel.
When exactly is a pixel drawn?
I said I need to make that all pixels which intersect a line need to be drawn. But there's some ambiguity in the edge cases.
Here is a list of all possible intersections in which a pixel will be drawn for a line where Qx >= Px & Qy >= Py:
A - If a line intersects the pixel completely, the pixel will be drawn.
B - If a vertical line intersects the pixel completely, the pixel will be drawn.
C - If a horizontal line intersects the pixel completely, the pixel will be drawn.
D - If a vertical line perfectly touches the left of the pixel, the pixel will be drawn.
E - If a horizontal line perfectly touches the bottom of the pixel, the pixel will be drawn.
F - If a line endpoint starts inside of a pixel going (+,+), the pixel will be drawn.
G - If a line endpoint starts exactly on the left side of a pixel going (+,+), the pixel will be drawn.
H - If a line endpoint starts exactly on the bottom side of a pixel going (+,+), the pixel will be drawn.
I - If a line endpoint starts exactly on the bottom left corner of a pixel going (+,+), the pixel will be drawn.
And here are some pixels which do NOT intersect the line:
A' - If a line obviously doesn't intersect a pixel, the pixel will NOT be drawn.
B' - If a vertical line obviously doesn't intersect a pixel, the pixel will NOT be drawn.
C' - If a horizontal line obviously doesn't intersect a pixel, the pixel will NOT be drawn.
D' - If a vertical line exactly touches the right side of a pixel, the pixel will NOT be drawn.
E' - If a horizontal line exactly touches the top side of a pixel, the pixel will NOT be drawn.
F' - If a line endpoint starts exactly on the top right corner of a pixel going in the (+,+) direction, the pixel will NOT be drawn.
G' - If a line endpoint starts exactly on the top side of a pixel going in the (+,+) direction, the pixel will NOT be drawn.
H' - If a line endpoint starts exactly on the right side of a pixel going in the (+,+) direction, the pixel will NOT be drawn.
I' - If a line exactly touches a corner of the pixel, the pixel will NOT be drawn. This applies to all corners.
Those rules apply as you would expect (just flip the image) for the other quadrants. The problem I need to highlight is when an endpoint lies exactly on the edge of a pixel. Take a look at this case:
This is like image G' above, except the y-axis is flipped because the Qy < Py. There are 4x4 red dots because W is 4, making the pixel dimensions 4x4. Each of the 4 dots are the ONLY endpoints a line can touch. The line drawn goes from (1.25, 1.0) to (somewhere).
This shows why it's incorrect (at least how I defined pixel-line intersections) to say the pixel endpoints can be calculated as the floor of the line endpoints. The floored pixel coordinate for that endpoint seems to be (1,1), but it is clear that the line never really intersects that pixel. It just touches it, so I don't want to draw it.
Instead of flooring the line endpoints, you need to floor the minimal endpoints, and ceil the maximal endpoints minus 1 across both x & y dimensions.
So finally here is the complete code which does this flooring/ceiling:
int dx = x2 - x1;
int dy = y2 - y1;
int dx_x = (dx >= 0) ? 1 : -1;
int dy_y = (dy >= 0) ? 1 : -1;
int local_x = x1 % square_width;
int local_y = y1 % square_width;
int x_dist = (dx >= 0) ? (square_width - local_x) : (local_x);
int y_dist = (dy >= 0) ? (square_width - local_y) : (local_y);
int cross_product = abs(dx) * abs(y_dist) - abs(dy) * abs(x_dist);
dx_cross = -abs(dy) * square_width;
dy_cross = abs(dx) * square_width;
int x = x1 / square_width;
int y = y1 / square_width;
int end_x = x2 / square_width;
int end_y = y2 / square_width;
// Perform ceiling/flooring of the pixel endpoints
if (dy < 0)
{
if ((y1 % square_width) == 0)
{
y--;
cross_product += dy_cross;
}
}
else if (dy > 0)
{
if ((y2 % square_width) == 0)
end_y--;
}
if (dx < 0)
{
if ((x1 % square_width) == 0)
{
x--;
cross_product += dx_cross;
}
}
else if (dx > 0)
{
if ((x2 % square_width) == 0)
end_x--;
}
while (x != end_x || y != end_y) {
SetPixel(x,y,color);
int old_cross = cross_product;
if (old_cross >= 0) {
x += dx_x;
cross_product += dx_cross;
}
if (old_cross <= 0) {
y += dy_y;
cross_product += dy_cross;
}
}
This code itself hasn't been tested, but it comes slightly modified from my GitHub project where it has been tested.
Let's assume you want to draw a line from P1 = (x1, y1) to P2 = (x2, y2) where all the numbers are floating point pixel coordinates.
Calculate the true pixel coordinates of P1 and P2 and paint them: P* = (round(x), round(y)).
If abs(x1* - x2*) <= 1 && abs(y1* - y2*) <= 1 then you are finished.
Decide whether it is a horizontal (true) or a vertical line (false): abs(x1 - x2) >= abs(y1 - y2).
If it is a horizontal line and x1 > x2 or if it is a vertical line and y1 > y2: swap P1 with P2 (and also P1* with P2*).
If it is a horizontal line you can get the y-coordinates for all the x-coordinates between x1* and x2* with the following formula:
y(x) = round(y1 + (x - x1) / (x2 - x1) * (y2 - y1))
If you have a vertical line you can get the x-coordinates for all the y-coordinates between y1* and y2* with this formula:
x(y) = round(x1 + (y - y1) / (y2 - y1) * (x2 - x1))
Here is a demo you can play around with, you can try different points on line 12.

Understanding Bresenham's error accumulation part of the algorithm?

I'm having issues understanding how the error accumulation part works in Bresenham's line drawing algorithm.
Say we have x1 and x2. Let's assume that x1 < x2, y1 < y2, and (x2 - x1) >= (y2 - y1) for simplicity:
Let's start with the naive way of drawing a line. It would look something like:
void DrawLine(int x1, int y1, int x2, int y2)
{
float y = y1 + 0.5f;
float slope = (float)(y2 - y1) / (x2 - x1);
for (int x = x1; x <= x2; ++x)
{
PlotPixel(x, (int)y);
y += slope;
}
}
Let's make it more Bresenham'ish, and separate the integer and floating-point parts of y:
void DrawLine(int x1, int y1, int x2, int y2)
{
int yi = y1;
float yf = 0.5f;
float slope = (float)(y2 - y1) / (x2 - x1);
for (int x = x1; x <= x2; ++x)
{
PlotPixel(x, yi);
yf += slope;
if (yf >= 1.0f)
{
yf -= 1.0f;
++yi;
}
}
}
At this point we could multiply yf and slope by 2 * (x2 - x1) to make them integers, no more floats. I understand that.
The part I don't fully understand, is this:
if (yf >= 1.0f)
{
yf -= 1.0f;
++yi;
}
How does that actually work? why are we comparing against 1.0 and then decrementing by it?
I know that the basic question of Bresenham is: If we're currently at pixel x, y and we want to draw the next one, should we pick x + 1, y or x + 1, y + 1? - I just don't understand how that check is helping us answer this question.
Some people call it error term, some call it threshold, I just don't get what it represents.
Any explanations is appreciated,
thanks.
Bresenham's line rasterization algorithm performs all the calculations in integer arithmetic. In your code you are using float types and you shouldn't.
First consider that you know two pixels that are on the line. The starting pixel and the end pixel. What the algorithm calculates are the pixels that approximate the line such that the rasterized line starts and stops on the two input pixels.
Second, all lines drawn are reflections of lines with slope between 0 and 0.5. There is a special case for vertical lines. If your algorithm is correct for this input, then you need to initialize the starting state of the rasterizer to correctly rasterize a line: start pixel (x, y), ∆x, ∆y, and D the decision variable.
Since you can assume all lines are drawn from left to right, have positive slope equal to or less than 0.5, the problem boils down to:
is the next rasterized pixel to the current pixels right or to the right and up one pixel.
You can make this decision by keeping track of how much your rasterized line deviates from the true line. To do so, the line equation is re-written into an implicit function, F(x, y) = ∆yx - ∆xy + ∆xb = 0 and you repeatedly evaluate it F(x + 1 y + 0.5). Since that requires floating point math, you focus on identifying if you are on, above, or below the true line. Therefore, F(x + 1 y + 0.5) = ∆y - 0.5∆x and multiplying by two 2 * F(x + 1 y + 0.5) = 2∆y - ∆x. That's the first decision, if the result is less than zero, add one to x but zero to y.
The second decision and subsequent decisions follow similarly and the error is accumulated. A decision variable D is initialized to 2∆y - ∆x. If D < 0, then D = D + 2∆y; else y = y + 1 and D = D + 2(∆y - ∆x). The x variable is always incremented.
Jim Arvo had a great explanation of Bresenham's algorithm.
In your implementation yf is a 0.5 + distance between real floating-point Y coordinate and drawn (integral) Y coordinate. This distance is the current error of your drawing. You want to keep the error within at most half-of-pixel between real line and drawn line (-0.5..+0.5), so your yf which is 0.5+error should be between 0 and 1. When it exceeds one, you just increase your drawn Y coordinate (yi) by one and you need to decrease an error by one. Let's take an example:
slope = 0.3;
x = 0; yf = 0.5; y = 0; // start drawing: no error
x = 1; yf = 0.8; y = 0; // draw second point at (1, 0); error is +0.3
x = 2; yf = 1.1; y = 0; // error is too big (+0.6): increase y
yf = 0.1; y = 1; // now error is -0.4; draw point at (2, 1)
x = 3; yf = 0.4; y = 1; // draw at (3, 1); error is -0.1
x = 4; yf = 0.7; y = 1; // draw at (4, 1); error is +0.2
x = 5; yf = 1.0; y = 1; // error is too big (+0.5); increase y
yf = 0.0; y = 2; // now error is -0.5; draw point at (5, 2)
And so on.

Draw a sphere using 3D pixels (voxels)

Can you suggest an algorithm that can draw a sphere in 3D space using only the basic plot(x,y,z) primitive (which would draw a single voxel)?
I was hoping for something similar to Bresenham's circle algorithm, but for 3D instead of 2D.
FYI, I'm working on a hardware project that is a low-res 3D display using a 3-dimensional matrix of LEDs, so I need to actually draw a sphere, not just a 2D projection (i.e. circle).
The project is very similar to this:
... or see it in action here.
One possibility I have in mind is this:
calculate the Y coordinates of the poles (given the radius) (for a sphere centered in the origin, these would be -r and +r)
slice the sphere: for each horizontal plane pi between these coordinates, calculate the radius of the circle obtained by intersecting said plane with the sphere => ri.
draw the actual circle of radius ri on plane pi using Bresenham's algorithm.
FWIW, I'm using a .NET micro-framework microprocessor, so programming is C#, but I don't need answers to be in C#.
The simple, brute force method is to loop over every voxel in the grid and calculate its distance from the sphere center. Then color the voxel if its distance is less than the sphere radius. You can save a lot of instructions by eliminating the square root and comparing the dot product to the radius squared.
Pretty far from optimal, sure. But on an 8x8x8 grid as shown, you'll need to do this operation 512 times per sphere. If the sphere center is on the grid, and its radius is an integer, you only need integer math. The dot product is 3 multiplies and 2 adds. Multiplies are slow; let's say they take 4 instructions each. Plus you need a comparison. Add in the loads and stores, let's say it costs 20 instructions per voxel. That's 10240 instructions per sphere.
An Arduino running at 16 MHz could push 1562 spheres per second. Unless you're doing tons of other math and I/O, this algorithm should be good enough.
I don't believe running the midpoint circle algorithm on each layer will give the desired results once you reach the poles, as you will have gaps in the surface where LEDs are not lit. This may give the result you want, however, so that would be up to aesthetics. This post is based on using the midpoint circle algorithm to determine the radius of the layers through the middle two vertical octants, and then when drawing each of those circles also setting the points for the polar octants.
I think based on #Nick Udall's comment and answer here using the circle algorithm to determine radius of your horizontal slice will work with a modification I proposed in a comment on his answer. The circle algorithm should be modified to take as an input an initial error, and also draw the additional points for the polar octants.
Draw the standard circle algorithm points at y0 + y1 and y0 - y1: x0 +/- x, z0 +/- z, y0 +/- y1, x0 +/- z, z0 +/- x, y0 +/- y1, total 16 points. This forms the bulk of the vertical of the sphere.
Additionally draw the points x0 +/- y1, z0 +/- x, y0 +/- z and x0 +/- x, z0 +/- y1, y0 +/- z, total 16 points, which will form the polar caps for the sphere.
By passing the outer algorithm's error into the circle algorithm, it will allow for sub-voxel adjustment of each layer's circle. Without passing the error into the inner algorithm, the equator of the circle will be approximated to a cylinder, and each approximated sphere face on the x, y, and z axes will form a square. With the error included, each face given a large enough radius will be approximated as a filled circle.
The following code is modified from Wikipedia's Midpoint circle algorithm. The DrawCircle algorithm has the nomenclature changed to operate in the xz-plane, addition of the third initial point y0, the y offset y1, and initial error error0. DrawSphere was modified from the same function to take the third initial point y0 and calls DrawCircle rather than DrawPixel
public static void DrawCircle(int x0, int y0, int z0, int y1, int radius, int error0)
{
int x = radius, z = 0;
int radiusError = error0; // Initial error state passed in, NOT 1-x
while(x >= z)
{
// draw the 32 points here.
z++;
if(radiusError<0)
{
radiusError+=2*z+1;
}
else
{
x--;
radiusError+=2*(z-x+1);
}
}
}
public static void DrawSphere(int x0, int y0, int z0, int radius)
{
int x = radius, y = 0;
int radiusError = 1-x;
while(x >= y)
{
// pass in base point (x0,y0,z0), this algorithm's y as y1,
// this algorithm's x as the radius, and pass along radius error.
DrawCircle(x0, y0, z0, y, x, radiusError);
y++;
if(radiusError<0)
{
radiusError+=2*y+1;
}
else
{
x--;
radiusError+=2*(y-x+1);
}
}
}
For a sphere of radius 4 (which actually requires 9x9x9), this would run three iterations of the DrawCircle routine, with the first drawing a typical radius 4 circle (three steps), the second drawing a radius 4 circle with initial error of 0 (also three steps), and then the third drawing a radius 3 circle with initial error 0 (also three steps). That ends up being nine calculated points, drawing 32 pixels each.
That makes 32 (points per circle) x 3 (add or subtract operations per point) + 6 (add, subtract, shift operations per iteration) = 102 add, subtract, or shift operations per calculated point. In this example, that's 3 points for each circle = 306 operations per layer. The radius algorithm also adds 6 operations per layer and iterates 3 times, so 306 + 6 * 3 = 936 basic arithmetic operations for the example radius of 4.
The cost here is that you will repeatedly set some pixels without additional condition checks (i.e. x = 0, y = 0, or z = 0), so if your I/O is slow you may be better off adding the condition checks. Assuming all LEDs were cleared at the start, the example circle would set 288 LEDs, while there are many fewer LEDs that would actually be lit due to repeat sets.
It looks like this would perform better than the bruteforce method for all spheres that would fit in the 8x8x8 grid, but the bruteforce method would have consistent timing regardless of radius, while this method will slow down when drawing large radius spheres where only part will be displayed. As the display cube increases in resolution, however, this algorithm timing will stay consistent while bruteforce will increase.
Assuming that you already have a plot function like you said:
public static void DrawSphere(double r, int lats, int longs)
{
int i, j;
for (i = 0; i <= lats; i++)
{
double lat0 = Math.PI * (-0.5 + (double)(i - 1) / lats);
double z0 = Math.Sin(lat0) * r;
double zr0 = Math.Cos(lat0) * r;
double lat1 = Math.PI * (-0.5 + (double)i / lats);
double z1 = Math.Sin(lat1) * r;
double zr1 = Math.Cos(lat1) * r;
for (j = 0; j <= longs; j++)
{
double lng = 2 * Math.PI * (double)(j - 1) / longs;
double x = Math.Cos(lng);
double y = Math.Sin(lng);
plot(x * zr0, y * zr0, z0);
plot(x * zr1, y * zr1, z1);
}
}
}
That function should plot a sphere at the origin with specified latitude and longitude resolution (judging by your cube you probably want something around 40 or 50 as a rough guess). This algorithm doesn't "fill" the sphere though, so it will only provide an outline, but playing with the radius should let you fill the interior, probably with decreasing resolution of the lats and longs along the way.
Just found an old q&a about generating a Sphere Mesh, but the top answer actually gives you a short piece of pseudo-code to generate your X, Y and Z :
(x, y, z) = (sin(Pi * m/M) cos(2Pi * n/N), sin(Pi * m/M) sin(2Pi * n/N), cos(Pi * m/M))
Check this Q&A for details :)
procedurally generate a sphere mesh
My solution uses floating point math instead of integer math not ideal but it works.
private static void DrawSphere(float radius, int posX, int poxY, int posZ)
{
// determines how far apart the pixels are
float density = 1;
for (float i = 0; i < 90; i += density)
{
float x1 = radius * Math.Cos(i * Math.PI / 180);
float y1 = radius * Math.Sin(i * Math.PI / 180);
for (float j = 0; j < 45; j += density)
{
float x2 = x1 * Math.Cos(j * Math.PI / 180);
float y2 = x1 * Math.Sin(j * Math.PI / 180);
int x = (int)Math.Round(x2) + posX;
int y = (int)Math.Round(y1) + posY;
int z = (int)Math.Round(y2) + posZ;
DrawPixel(x, y, z);
DrawPixel(x, y, -z);
DrawPixel(-x, y, z);
DrawPixel(-x, y, -z);
DrawPixel(z, y, x);
DrawPixel(z, y, -x);
DrawPixel(-z, y, x);
DrawPixel(-z, y, -x);
DrawPixel(x, -y, z);
DrawPixel(x, -y, -z);
DrawPixel(-x, -y, z);
DrawPixel(-x, -y, -z);
DrawPixel(z, -y, x);
DrawPixel(z, -y, -x);
DrawPixel(-z, -y, x);
DrawPixel(-z, -y, -x);
}
}
}

Resources