How do I initialize the t-variables in "A Fast Voxel Traversal Algorithm for Ray Tracing"? - algorithm

I am trying to implement the algorithm explained on this paper, used to traverse grid cells in order following a straight line, which is useful for ray tracing:
http://www.cse.yorku.ca/~amana/research/grid.pdf
The paper describes the algorithm as two parts: initialisation and iterative traversal. I can undersand the iterative traversal part, but I'm having trouble understanding how some of the variables in the initialisation part are calculated.
I need help initialising tMaxX, tMaxY, tDeltaX & tDeltaY. Their initialisation procedure is explained as follows:
Next, we determine the value of t at which the ray crosses the first
vertical voxel boundary and store it in variable tMaxX. We perform a
similar computation in y and store the result in tMaxY. The minimum of
these two values will indicate how much we can travel along the ray
and still remain in the current voxel.
Finally, we compute tDeltaX and tDeltaY. TDeltaX indicates how far
along the ray we must move (in units of t) for the horizontal
component of such a movement to equal the width of a voxel. Similarly,
store in tDeltaY the amount of movement along the ray which has a
vertical component equal to the height of a voxel.
I'm not able to deduce the code I need form the English description given above. Can someone translate it to a math/pseudocode expression for me?

Initialization for X-coordinate variables (the same for Y)
DX = X2 - X1
tDeltaX = GridCellWidth / DX
tMaxX = tDeltaX * (1.0 - Frac(X1 / GridCellWidth))
//Frac if fractional part of float, for example, Frac(1.3) = 0.3, Frac(-1.7)=0.3
Example:
GridCellWidth, Height = 20
X1 = 5, X2 = 105
Y1 = 5, Y2 = 55
DX = 100, DY = 50
tDeltaX = 0.2, tDeltaY = 0.4
tMaxX = 0.2 * (1.0 - 0.25) = 0.15 //ray will meet first vertical line at this param
tMaxY = 0.4 * (1.0 - 0.25) = 0.3 //ray will meet first horizontal line at this param
We can see that first cell border will be met at parameter t = 0.15

The one that worked for me in 3D for both positive and negative directions (in CUDA C):
#define SIGN(x) (x > 0 ? 1 : (x < 0 ? -1 : 0))
#define FRAC0(x) (x - floorf(x))
#define FRAC1(x) (1 - x + floorf(x))
float tMaxX, tMaxY, tMaxZ, tDeltaX, tDeltaY, tDeltaZ;
int3 voxel;
float x1, y1, z1; // start point
float x2, y2, z2; // end point
int dx = SIGN(x2 - x1);
if (dx != 0) tDeltaX = fmin(dx / (x2 - x1), 10000000.0f); else tDeltaX = 10000000.0f;
if (dx > 0) tMaxX = tDeltaX * FRAC1(x1); else tMaxX = tDeltaX * FRAC0(x1);
voxel.x = (int) x1;
int dy = SIGN(y2 - y1);
if (dy != 0) tDeltaY = fmin(dy / (y2 - y1), 10000000.0f); else tDeltaY = 10000000.0f;
if (dy > 0) tMaxY = tDeltaY * FRAC1(y1); else tMaxY = tDeltaY * FRAC0(y1);
voxel.y = (int) y1;
int dz = SIGN(z2 - z1);
if (dz != 0) tDeltaZ = fmin(dz / (z2 - z1), 10000000.0f); else tDeltaZ = 10000000.0f;
if (dz > 0) tMaxZ = tDeltaZ * FRAC1(z1); else tMaxZ = tDeltaZ * FRAC0(z1);
voxel.z = (int) z1;
while (true) {
if (tMaxX < tMaxY) {
if (tMaxX < tMaxZ) {
voxel.x += dx;
tMaxX += tDeltaX;
} else {
voxel.z += dz;
tMaxZ += tDeltaZ;
}
} else {
if (tMaxY < tMaxZ) {
voxel.y += dy;
tMaxY += tDeltaY;
} else {
voxel.z += dz;
tMaxZ += tDeltaZ;
}
}
if (tMaxX > 1 && tMaxY > 1 && tMaxZ > 1) break;
// process voxel here
}
Note, grid cell's width/height/depth are all equal to 1 in my case, but you can easily modify it for your needs.

Related

Loop through a array in circle shape without repeat indexes

I need to loop through a array in circle in arc shape with a small radius (like draw a circle pixel by pixel), but all algorithm i tried, checks duplicate indexes of array (it's got the same x and y several times).
I have a radius of 3, with a circle form of 28 elements (not filled), but the algorithm iterate 360 times. I can check if x or y change before i do something, but it's lame.
My code now:
for (int radius = 1; radius < 6; radius++)
{
for (double i = 0; i < 360; i += 1)
{
double angle = i * System.Math.PI / 180;
int x = (int)(radius * System.Math.Cos(angle)) + centerX;
int y = (int)(radius * System.Math.Sin(angle)) + centerY;
// do something
// if (array[x, y]) ....
}
}
PS: I can't use midpoint circle, because i need to increment radius starting from 2 until 6, and not every index is obtained, because his circle it's not real (according trigonometry)
EDIT:
What i really need, is scan a full circle edge by edge, starting by center.
360 steps (it's get all coordinates):
Full scan
for (int radius = 2; radius <= 7; radius++)
{
for (double i = 0; i <= 360; i += 1)
{
double angle = i * System.Math.PI / 180;
int x = (int)(radius * System.Math.Cos(angle));
int y = (int)(radius * System.Math.Sin(angle));
print(x, y, "X");
}
}
Using Midpoint Circle or other algorithm skipping steps (missing coordinates):
Midpoint Circle Algorithm
for (int radius = 2; radius <= 7; radius++)
{
int x = radius;
int y = 0;
int err = 0;
while (x >= y)
{
print(x, y, "X");
print(y, x, "X");
print(-y, x, "X");
print(-y, x, "X");
print(-x, y, "X");
print(-x, -y, "X");
print(-y, -x, "X");
print(y, -x, "X");
print(x, -y, "X");
y += 1;
err += 1 + 2 * y;
if (2 * (err - x) + 1 > 0)
{
x -= 1;
err += 1 - 2 * x;
}
}
}
There are two algorithmic ideas in play here: one is rasterizing a circle. The OP code presents a couple opportunities for improvement on that front: (a) one needn't sample the entire 360 degree circle, realizing that a circle is symmetric across both axes. (x,y) can be reflected in the other three quadrants as (-x,y), (-x,-y), and (x,-y). (b) the step on the loop should be related to the curvature. A simple heuristic is to use the radius as the step. So...
let step = MIN(radius, 90)
for (double i=0; i<90; i += step) {
add (x,y) to results
reflect into quadrants 2,3,4 and add to results
}
With these couple improvements, you may no longer care about duplicate samples being generated. If you still do, then the second idea, independent of the circle, is how to hash a pair of ints. There's a good article about that here: Mapping two integers to one, in a unique and deterministic way.
In a nutshell, we compute an int from our x,y pair that's guaranteed to map uniquely, and then check that for duplicates...
cantor(x, y) = 1/2(x + y)(x + y + 1) + y
This works only for positive values of x,y, which is just what you need since we're only computing (and then reflecting) in the first quadrant. For each pair, check that they are unique
let s = an empty set
int step = MIN(radius, 90)
for (double i=0; i<90; i += step) {
generate (x,y)
let c = cantor(x,y)
if (not(s contains c)) {
add (x,y) to results
reflect into quadrants 2,3,4 and add to results
add c to s
}
}
Got it!
It's not beautiful, but work for me.
int maxRadius = 7;
for (int radius = 1; radius <= maxRadius; radius++)
{
x = position.X - radius;
y = position.Y - radius;
x2 = position.X + radius;
y2 = position.Y + radius;
for (int i = 0; i <= radius * 2; i++)
{
if (InCircle(position.X, position.Y, x + i, y, maxRadius)) // Top X
myArray[position, x + i, y]; // check array
if (InCircle(position.X, position.Y, x + i, y2, maxRadius)) // Bottom X
myArray[position, x + i, y2]; // check array
if (i > 0 && i < radius * 2)
{
if (InCircle(position.X, position.Y, x, y + i, maxRadius)) // Left Y
myArray[position, x, y + i]; // check array
if (InCircle(position.X, position.Y, x2, y + i, maxRadius)) // Right Y
myArray[position, x2, y + i]; // check array
}
}
}
public static bool InCircle(int originX, int originY, int x, int y, int radius)
{
int dx = Math.Abs(x - originX);
if (dx > radius) return false;
int dy = Math.Abs(y - originY);
if (dy > radius) return false;
if (dx + dy <= radius) return true;
return (dx * dx + dy * dy <= radius * radius);
}

Algorithm to access the tiles in a matrix (game map) that are in a disc

I am developping a tile mapped game.
I need to access the tiles that are in a disc, with a given radius and centered on a given point.
Accessing the tiles that are in a square is easy, we only need to use two loops :
for(int i=xmin; i<xmax; ++i)
for(int j=ymin; j<ymax; ++j)
// the tile map[i][j] is in the square
But how do you access the tiles that are in a given disc (full circle) ?
EDIT:
I mean, I could process each tile in a bounding rectangle (bounding the disc), and determine whether or not a tile in that rectangle is in the disk, by using (x-x0)²+(y-y0)²<R², but with that algorithm, we would explore useless tiles.
When using a large radius, there are many tiles to process, and it will be slow because calculating (x-x0)²+(y-y0)²<R² many times is heavy
What I want is an algorithm more efficient than this one.
EDIT2:
I don't need a perfect disk
We can do a linear scan through x, calculating the range of y. Then we only have to scan through the tiles that are in the circle, like in this badly drawn picture. (Christmas colors?)
If we have a circle with radius r and an x-position x, we can figure out the maximum length of y:
y = sqrt(r * r - x * x);
So the code for iterating through the tiles would look like:
int center_x = (xmin + xmax) / 2;
int center_y = (ymin + ymax) / 2;
for(int x = xmin; x <= xmax; x++) {
int ydist = sqrt(r * r - (center_x - x) * (center_x - x));
for(int y = center_y - ydist; y <= center_y + ydist; y++) {
// these are the tiles in the disc
}
}
Here's some Python code:
from Tkinter import *
from math import *
tk = Tk()
g = Canvas(tk, width=500, height=500)
g.pack()
x0 = 25 # x center
y0 = 25 # y center
r = 17 # radius
t = 10 # tile side length
for x in range(x0 - r, x0 + r + 1):
ydist = int(round(sqrt(r**2 - (x0 - x)**2), 1))
for y in range(y0 - ydist, y0 + ydist + 1):
g.create_rectangle(x * t, y * t, x * t + t, y * t + t
, fill='#'
+ '0123456789ABCDEF'[15 - int(15 * sqrt((x0 - x)**2 + (y0 - y)**2) / r)]
+ '0123456789ABCDEF'[int(15 * sqrt((x0 - x)**2 + (y0 - y)**2) / r)]
+ '0')
g.create_oval((x0 - r) * t, (y0 - r) * t, (x0 + r) * t + t, (y0 + r) * t + t, outline="red", width=2)
mainloop()
And the resulting disk:
Not perfect at the ends, but I hope it works well enough for you (or you can modify it).
You can use the Bresenham's circle Algorithm (section 3.3, Scan Converting Circles) (it uses integer arithmetic only, is very accurate and process fourth part of the whole circle to produce the entire circumference) in your tile matrix to detect those tiles that forms the circumference, then trace lines between them from up-to-down (or left-to-right):
The following is a pseudo implementation of the circle algorithm:
static void circle(int x0, int y0, int x1, int y1) {
// Bresenham's Circle Algorithm
int x, y, d, deltaE, deltaSE;
int radius, center_x, center_y;
bool change_x = false;
bool change_y = false;
if( x0 > x1 ) {
// swap x values
x = x0;
x0 = x1;
x1 = x;
change_x = true;
}
if( y0 > y1 ) {
// swap y values
y = y0;
y0 = y1;
y1 = y;
change_y = true;
}
int dx = x1 - x0;
int dy = y1 - y0;
radius = dx > dy ? (dy >> 1) : (dx >> 1);
center_x = change_x ? x0 - radius : x0 + radius;
center_y = change_y ? y0 - radius : y0 + radius;
x = 0;
y = radius;
d = 1 - radius;
deltaE = 3;
// -2 * radius + 5
deltaSE = -(radius << 1) + 5;
while(y > x) {
if(d < 0) {
d += deltaE;
deltaE += 2;
deltaSE += 2;
x++;
} else {
d += deltaSE;
deltaE += 2;
deltaSE += 4;
x++;
y--;
}
checkTiles(x, y, center_x, center_y);
}
}
void checkTiles(int x, int y, int center_x, int center_y) {
// here, you iterate tiles up-to-down from ( x + center_x, -y + center_y) to (x + center_x, y + center_y)
// in one straigh line using a for loop
for (int j = -y + center_y; j < y + center_y; ++j)
checkTileAt(x + center_x, j);
// Iterate tiles up-to-down from ( y + center_x, -x + center_y) to ( y + center_x, x + center_y)
for (int j = -x + center_y; j < x + center_y; ++j)
checkTileAt(y + center_x, j);
// Iterate tiles up-to-down from (-x + center_x, -y + center_y) to (-x + center_x, y + center_y)
for (int j = -y + center_y; j < y + center_y; ++j)
checkTileAt(-x + center_x, j);
// here, you iterate tiles up-to-down from (-y + center_x, -x + center_y) to (-y + center_x, x + center_y)
for (int j = -x + center_y; j < x + center_y; ++j)
checkTileAt(-y + center_x, j);
}
With this technique you should process only the required tiles (and after processing only a quarter of the circle), none unnecessary tiles would be checked. Beside that, it uses integer arithmetic only, wich makes it really fast (the deduction and explanation can be found in the provided book link) and the generated circumference is proven to be the best approximation for the real one.
Excluding tiles outside the square wont be much faster. I would just use a square but ignore tiles outside the circle. (e.g. by checking how far the tile is from the circle center)
for(int i=xmin; i<xmax; ++i):
for(int j=ymin; j<ymax; ++j):
if map[i][j] not in the circle:
break
// the tile map[i][j] is in the square
A rough estimate on performance overhead:
Area Square = 2*r*2*r
Area Circle = pi*r*r
Area Square / Area Circle = 4/pi = 1.27
This means using a square instead of a circle is only 1.27 times slower (assuming using a circle doesn't have its own inefficiencies)
Also because you will likely perform some operation on the tiles, (making the iterations involving tiles in the circle much slower) it means the performance gain will go down to almost 0 using a circle layout instead of a square layout.
Use a bounding octagon. It's the bounding square with corners cut off. You need these tests for if a point (any corner of a tile) is in that shape. Put this inside the 2D loop.
abs(x) < R
abs(y) < R
abs(x)+abs(y) < sqrt(2)*R
Precalculate sqrt(2)*R, of course.
This isn't the same as a circle, obviously, but cuts down nicely the amount of wasted space compared to a square.
It'll be hard to generate a loop that goes over only the tile centers or tile corners perfectly, without needing some sort of test in the loop. Any hope for writing such loops would be from use Bresenham's algorithm.

Generate a random point on a rectangle's perimeter with uniform distribution

Given any particular rectangle (x1,y1)-(x2,y2), how can I generate a random point on its perimeter?
I've come up with a few approaches, but it seems like there ought to be a pretty canonical way to do it.
First, I thought I'd generate a random point within the rectangle and clamp it to the closest side, but the distribution didn't seem uniform (points almost never fell on the shorter sides). Second, I picked a side at random and then chose a random point on that side. The code was kind of clunky and it wasn't uniform either - but in the exact opposite way (short sides had the same chance of getting points as long sides). Finally, I've been thinking about "unfolding" the rectangle into a single line and picking a random point on the line. I think that would generate a uniform distribution, but I thought I'd ask here before embarking down that road.
Your last approach is what I would have recommended just from reading your title. Go with that. Your second approach (pick a side at random) would work if you picked a side with probability proportional to the side length.
here is the unfolding idea in objective-c, seems to work, doesn't it :)
//randomness macro
#define frandom (float)arc4random()/UINT64_C(0x100000000)
#define frandom_range(low,high) ((high-low)*frandom)+low
//this will pick a random point on the rect edge
- (CGPoint)pickPointOnRectEdge:(CGRect)edge {
CGPoint pick = CGPointMake(edge.origin.x, edge.origin.y);
CGFloat a = edge.size.height;
CGFloat b = edge.size.width;
CGFloat edgeLength = 2*a + 2*b;
float randomEdgeLength = frandom_range(0.0f, (float)edgeLength);
//going from bottom left counter-clockwise
if (randomEdgeLength<a) {
//left side a1
pick = CGPointMake(edge.origin.x, edge.origin.y + a);
} else if (randomEdgeLength < a+b) {
//top side b1
pick = CGPointMake(edge.origin.x + randomEdgeLength - a, edge.origin.y + edge.size.height );
} else if (randomEdgeLength < (a + b) + a) {
//right side a2
pick = CGPointMake(edge.origin.x + edge.size.width, edge.origin.y + randomEdgeLength - (a+b));
} else {
//bottom side b2
pick = CGPointMake(edge.origin.x + randomEdgeLength - (a + b + a), edge.origin.y);
}
return pick;
}
If by 'random point on the perimeter' you do in fact mean 'point selected from a uniform random distribution over the length of the perimeter', then yes, your 'unfolding' approach is correct.
It should be mentioned however that both your previous approaches do qualify as being a 'random point on the perimeter', just with a non-uniform distribution.
Figured I would try to do this without branching, expressing both X and Y coords as a function of the random number that walks the "unfolded" rectangle.
JS:
function randomOnRect() {
let r = Math.random();
return [Math.min(1, Math.max(0, Math.abs((r * 4 - .5) % 4 - 2) - .5)),
Math.min(1, Math.max(0, Math.abs((r * 4 + .5) % 4 - 2) - .5))]
}
Your last suggestion seems best to me.
Look at the perimeter as a single long line [of length 2*a + 2*b], generate a random number within it, calculate where the point is on the rectangle [assume it starts from some arbitrary point, it doesn't matter which].
It requires only one random and thus is relatively cheap [random sometimes are costly operations].
It is also uniform, and trivial to prove it, there is an even chance the random will get you to each point [assuming the random function is uniform, of course].
For example:
static Random random = new Random();
/** returns a point (x,y) uniformly distributed
* in the border of the rectangle 0<=x<=a, 0<=y<=b
*/
public static Point2D.Double randomRect(double a, double b) {
double x = random.nextDouble() * (2 * a + 2 * b);
if (x < a)
return new Point2D.Double(x, 0);
x -= a;
if (x < b)
return new Point2D.Double(a, x);
x -= b;
if (x < a)
return new Point2D.Double(x, b);
else
return new Point2D.Double(0, x-a);
}
Here is my implementation with uniform distribution (assumes x1 < x2 and y1 < y2):
void randomPointsOnPerimeter(int x1, int y1, int x2, int y2) {
int width = abs(x2 - x1);
int height = abs(y2 - y1);
int perimeter = (width * 2) + (height * 2);
// number of points proportional to perimeter
int n = (int)(perimeter / 8.0f);
for (int i = 0; i < n; i++) {
int x, y;
int dist = rand() % perimeter;
if (dist <= width) {
x = (rand() % width) + x1;
y = y1;
} else if (dist <= width + height) {
x = x2;
y = (rand() % height) + y1;
} else if (dist <= (width * 2) + height) {
x = (rand() % width) + x1;
y = y2;
} else {
x = x1;
y = (rand() % height) + y1;
}
// do something with (x, y)...
}
}
Here's my implementation in Javascript
function pickPointOnRectEdge(width,height){
var randomPoint = Math.random() * (width * 2 + height * 2);
if (randomPoint > 0 && randomPoint < height){
return {
x: 0,
y: height - randomPoint
}
}
else if (randomPoint > height && randomPoint < (height + width)){
return {
x: randomPoint - height,
y: 0
}
}
else if (randomPoint > (height + width) && randomPoint < (height * 2 + width)){
return {
x: width,
y: randomPoint - (width + height)
}
}
else {
return {
x: width - (randomPoint - (height * 2 + width)),
y: height
}
}
}

Circle-circle intersection points

How do I calculate the intersection points of two circles. I would expect there to be either two, one or no intersection points in all cases.
I have the x and y coordinates of the centre-point, and the radius for each circle.
An answer in python would be preferred, but any working algorithm would be acceptable.
Intersection of two circles
Written by Paul Bourke
The following note describes how to find the intersection point(s)
between two circles on a plane, the following notation is used. The
aim is to find the two points P3 = (x3,
y3) if they exist.
First calculate the distance d between the center
of the circles. d = ||P1 - P0||.
If d > r0 + r1 then there are no solutions,
the circles are separate. If d < |r0 -
r1| then there are no solutions because one circle is
contained within the other. If d = 0 and r0 =
r1 then the circles are coincident and there are an
infinite number of solutions.
Considering the two triangles P0P2P3
and P1P2P3 we can write
a2 + h2 = r02 and
b2 + h2 = r12
Using d = a + b we can solve for a, a =
(r02 - r12 +
d2 ) / (2 d)
It can be readily shown that this reduces to
r0 when the two circles touch at one point, ie: d =
r0 + r1
Solve for h by substituting a into the first
equation, h2 = r02 - a2
So P2 = P0 + a ( P1 -
P0 ) / d And finally, P3 =
(x3,y3) in terms of P0 =
(x0,y0), P1 =
(x1,y1) and P2 =
(x2,y2), is x3 =
x2 +- h ( y1 - y0 ) / d
y3 = y2 -+ h ( x1 - x0 ) /
d
Source: http://paulbourke.net/geometry/circlesphere/
Here is my C++ implementation based on Paul Bourke's article. It only works if there are two intersections, otherwise it probably returns NaN NAN NAN NAN.
class Point{
public:
float x, y;
Point(float px, float py) {
x = px;
y = py;
}
Point sub(Point p2) {
return Point(x - p2.x, y - p2.y);
}
Point add(Point p2) {
return Point(x + p2.x, y + p2.y);
}
float distance(Point p2) {
return sqrt((x - p2.x)*(x - p2.x) + (y - p2.y)*(y - p2.y));
}
Point normal() {
float length = sqrt(x*x + y*y);
return Point(x/length, y/length);
}
Point scale(float s) {
return Point(x*s, y*s);
}
};
class Circle {
public:
float x, y, r, left;
Circle(float cx, float cy, float cr) {
x = cx;
y = cy;
r = cr;
left = x - r;
}
pair<Point, Point> intersections(Circle c) {
Point P0(x, y);
Point P1(c.x, c.y);
float d, a, h;
d = P0.distance(P1);
a = (r*r - c.r*c.r + d*d)/(2*d);
h = sqrt(r*r - a*a);
Point P2 = P1.sub(P0).scale(a/d).add(P0);
float x3, y3, x4, y4;
x3 = P2.x + h*(P1.y - P0.y)/d;
y3 = P2.y - h*(P1.x - P0.x)/d;
x4 = P2.x - h*(P1.y - P0.y)/d;
y4 = P2.y + h*(P1.x - P0.x)/d;
return pair<Point, Point>(Point(x3, y3), Point(x4, y4));
}
};
Why not just use 7 lines of your favorite procedural language (or programmable calculator!) as below.
Assuming you are given P0 coords (x0,y0), P1 coords (x1,y1), r0 and r1 and you want to find P3 coords (x3,y3):
d=sqr((x1-x0)^2 + (y1-y0)^2)
a=(r0^2-r1^2+d^2)/(2*d)
h=sqr(r0^2-a^2)
x2=x0+a*(x1-x0)/d
y2=y0+a*(y1-y0)/d
x3=x2+h*(y1-y0)/d // also x3=x2-h*(y1-y0)/d
y3=y2-h*(x1-x0)/d // also y3=y2+h*(x1-x0)/d
Here's an implementation in Javascript using vectors. The code is well documented, you should be able to follow it. Here's the original source
See live demo here:
// Let EPS (epsilon) be a small value
var EPS = 0.0000001;
// Let a point be a pair: (x, y)
function Point(x, y) {
this.x = x;
this.y = y;
}
// Define a circle centered at (x,y) with radius r
function Circle(x,y,r) {
this.x = x;
this.y = y;
this.r = r;
}
// Due to double rounding precision the value passed into the Math.acos
// function may be outside its domain of [-1, +1] which would return
// the value NaN which we do not want.
function acossafe(x) {
if (x >= +1.0) return 0;
if (x <= -1.0) return Math.PI;
return Math.acos(x);
}
// Rotates a point about a fixed point at some angle 'a'
function rotatePoint(fp, pt, a) {
var x = pt.x - fp.x;
var y = pt.y - fp.y;
var xRot = x * Math.cos(a) + y * Math.sin(a);
var yRot = y * Math.cos(a) - x * Math.sin(a);
return new Point(fp.x+xRot,fp.y+yRot);
}
// Given two circles this method finds the intersection
// point(s) of the two circles (if any exists)
function circleCircleIntersectionPoints(c1, c2) {
var r, R, d, dx, dy, cx, cy, Cx, Cy;
if (c1.r < c2.r) {
r = c1.r; R = c2.r;
cx = c1.x; cy = c1.y;
Cx = c2.x; Cy = c2.y;
} else {
r = c2.r; R = c1.r;
Cx = c1.x; Cy = c1.y;
cx = c2.x; cy = c2.y;
}
// Compute the vector <dx, dy>
dx = cx - Cx;
dy = cy - Cy;
// Find the distance between two points.
d = Math.sqrt( dx*dx + dy*dy );
// There are an infinite number of solutions
// Seems appropriate to also return null
if (d < EPS && Math.abs(R-r) < EPS) return [];
// No intersection (circles centered at the
// same place with different size)
else if (d < EPS) return [];
var x = (dx / d) * R + Cx;
var y = (dy / d) * R + Cy;
var P = new Point(x, y);
// Single intersection (kissing circles)
if (Math.abs((R+r)-d) < EPS || Math.abs(R-(r+d)) < EPS) return [P];
// No intersection. Either the small circle contained within
// big circle or circles are simply disjoint.
if ( (d+r) < R || (R+r < d) ) return [];
var C = new Point(Cx, Cy);
var angle = acossafe((r*r-d*d-R*R)/(-2.0*d*R));
var pt1 = rotatePoint(C, P, +angle);
var pt2 = rotatePoint(C, P, -angle);
return [pt1, pt2];
}
Try this;
def ri(cr1,cr2,cp1,cp2):
int1=[]
int2=[]
ori=0
if cp1[0]<cp2[0] and cp1[1]!=cp2[1]:
p1=cp1
p2=cp2
r1=cr1
r2=cr2
if cp1[1]<cp2[1]:
ori+=1
elif cp1[1]>cp2[1]:
ori+=2
elif cp1[0]>cp2[0] and cp1[1]!=cp2[1]:
p1=cp2
p2=cp1
r1=cr2
r2=cr1
if p1[1]<p2[1]:
ori+=1
elif p1[1]>p2[1]:
ori+=2
elif cp1[0]==cp2[0]:
ori+=4
if cp1[1]>cp2[1]:
p1=cp1
p2=cp2
r1=cr1
r2=cr2
elif cp1[1]<cp2[1]:
p1=cp2
p2=cp1
r1=cr2
r2=cr1
elif cp1[1]==cp2[1]:
ori+=3
if cp1[0]>cp2[0]:
p1=cp2
p2=cp1
r1=cr2
r2=cr1
elif cp1[0]<cp2[0]:
p1=cp1
p2=cp2
r1=cr1
r2=cr2
if ori==1:#+
D=calc_dist(p1,p2)
tr=r1+r2
el=tr-D
a=r1-el
b=r2-el
A=a+(el/2)
B=b+(el/2)
thta=math.degrees(math.acos(A/r1))
rs=p2[1]-p1[1]
rn=p2[0]-p1[0]
gd=rs/rn
yint=p1[1]-((gd)*p1[0])
dty=calc_dist(p1,[0,yint])
aa=p1[1]-yint
bb=math.degrees(math.asin(aa/dty))
d=90-bb
e=180-d-thta
g=(dty/math.sin(math.radians(e)))*math.sin(math.radians(thta))
f=(g/math.sin(math.radians(thta)))*math.sin(math.radians(d))
oty=yint+g
h=f+r1
i=90-e
j=180-90-i
l=math.sin(math.radians(i))*h
k=math.cos(math.radians(i))*h
iy2=oty-l
ix2=k
int2.append(ix2)
int2.append(iy2)
m=90+bb
n=180-m-thta
p=(dty/math.sin(math.radians(n)))*math.sin(math.radians(m))
o=(p/math.sin(math.radians(m)))*math.sin(math.radians(thta))
q=p+r1
r=90-n
s=math.sin(math.radians(r))*q
t=math.cos(math.radians(r))*q
otty=yint-o
iy1=otty+s
ix1=t
int1.append(ix1)
int1.append(iy1)
elif ori==2:#-
D=calc_dist(p1,p2)
tr=r1+r2
el=tr-D
a=r1-el
b=r2-el
A=a+(el/2)
B=b+(el/2)
thta=math.degrees(math.acos(A/r1))
rs=p2[1]-p1[1]
rn=p2[0]-p1[0]
gd=rs/rn
yint=p1[1]-((gd)*p1[0])
dty=calc_dist(p1,[0,yint])
aa=yint-p1[1]
bb=math.degrees(math.asin(aa/dty))
c=180-90-bb
d=180-c-thta
e=180-90-d
f=math.tan(math.radians(e))*p1[0]
g=math.sqrt(p1[0]**2+f**2)
h=g+r1
i=180-90-e
j=math.sin(math.radians(e))*h
jj=math.cos(math.radians(i))*h
k=math.cos(math.radians(e))*h
kk=math.sin(math.radians(i))*h
l=90-bb
m=90-e
tt=l+m+thta
n=(dty/math.sin(math.radians(m)))*math.sin(math.radians(thta))
nn=(g/math.sin(math.radians(l)))*math.sin(math.radians(thta))
oty=yint-n
iy1=oty+j
ix1=k
int1.append(ix1)
int1.append(iy1)
o=bb+90
p=180-o-thta
q=90-p
r=180-90-q
s=(dty/math.sin(math.radians(p)))*math.sin(math.radians(o))
t=(s/math.sin(math.radians(o)))*math.sin(math.radians(thta))
u=s+r1
v=math.sin(math.radians(r))*u
vv=math.cos(math.radians(q))*u
w=math.cos(math.radians(r))*u
ww=math.sin(math.radians(q))*u
ix2=v
otty=yint+t
iy2=otty-w
int2.append(ix2)
int2.append(iy2)
elif ori==3:#y
D=calc_dist(p1,p2)
tr=r1+r2
el=tr-D
a=r1-el
b=r2-el
A=a+(el/2)
B=b+(el/2)
b=math.sqrt(r1**2-A**2)
int1.append(p1[0]+A)
int1.append(p1[1]+b)
int2.append(p1[0]+A)
int2.append(p1[1]-b)
elif ori==4:#x
D=calc_dist(p1,p2)
tr=r1+r2
el=tr-D
a=r1-el
b=r2-el
A=a+(el/2)
B=b+(el/2)
b=math.sqrt(r1**2-A**2)
int1.append(p1[0]+b)
int1.append(p1[1]-A)
int2.append(p1[0]-b)
int2.append(p1[1]-A)
return [int1,int2]
def calc_dist(p1,p2):
return math.sqrt((p2[0] - p1[0]) ** 2 +
(p2[1] - p1[1]) ** 2)

2D bounding box of a sector?

I've googled till I'm blue in the face, and unless I'm missing something really obvious, I can't find any algorithms for calculating the bounding box of a 2D sector.
Given the centre point of the enclosing circle, the radius, and the angles of the extent of the sector, what's the best algorithm to calculate the axis-aligned bounding rectangle of that sector?
Generate the following points:
The circle's center
The positions of the start and end angles of the sector
Additionally, for the angles among 0, 90, 180, and 270 that are within the angle range of the sector, their respective points on the sector
Calculate the min and max x and y from the above points. This is your bounding box
I'm going to rephrase yairchu's answer so that it is clearer (to me, anyway).
Ignore the center coordinates for now and draw the circle at the origin. Convince yourself of the following:
Anywhere the arc intersects an axis will be a max or a min.
If the arc doesn't intersect an axis, then the center will be one corner of the bounding rectangle, and this is the only case when it will be.
The only other possible extreme points of the sector to consider are the endpoints of the radii.
You now have at most 4+1+2 points to find. Find the max and min of those coordinates to draw the rectangle.
The rectangle is easily translated to the original circle by adding the coordinates of the center of the original circle to the rectangle's coordinates.
First of all I apologize if I commit mistakes writing but english is not my first language, spanish is actually!
I faced this problem, and I think I found an efficient solution.
First of all let's see an image of the situation
So we have an ellipse (actually a circle) and two points (C, D) which indicates our sector.
We also have the center of our circle (B) and the angle of the Arc alpha.
Now, in this case I made it passing through 360º on porpouse to see if it would work.
Let's say alpha -> -251.1º (it negative cause its clockwise), lets transform it to positive value 360º - 251.1º = 108.9º now our goal is to find the angle of the bisection of that angle so we can find the max point for the bounding box (E in the image), actually as you may have realized, the length of the segment BE equals the radius of the circle but we must have the angle to obtain the actual coordinates of the E point.
So 108.9º / 2 -> 54.45º now we have the angle.
To find the coordinates of E we use polar coordinates so
x = r * Cos(theta)
y = r * Sin(theta)
we have r and theta so we can calculate x and y
in my example r = 2.82… (actually it's irational but I took the first two decimal digits as a matter of ease)
We know our first radii is 87.1º so theta would be 87.1 - 54.45º -> 32.65º
we know *theta * is 32.65º so let's do some math
x = 2.82 * Cos(32.65º) -> 2.37552
y = 2.82 * Sin(32.65º) -> 1.52213
Now we need to adjust these values to the actual center of the circle so
x = x + centerX
y = y + centerY
In the example, the circle is centered at (1.86, 4.24)
x -> 4.23552
y -> 5.76213
At this stage we should use some calculus. We know that one of the edges of the bounding box will be a tangent of the arc that passes through the point we just calculated so, lets find that tangent (the red line).
We know that the tangent passes through our point (4.23, 5.76) now we need a slope.
As you can see, the slope is the same as the slope of the rect that passes through our radii's so we have to find that slope.
For doing that we need to get the coordinates of our radii's (a fast conversion to cartessian coordinates from polar coordinates).
x = r * Cos(theta)
y = r * Sin(theta)
So
p0 = (centerX + 2.82 * Cos(87.1º), centerY + 2.82 * Sin(87.1º))
p1 = (centerX + 2.82 * Cos(-21.8º), centerY + 2.82 * Sin(-21.8º))
(21.8º is the angle measured clockwise from the horizontal axis to the radii that is below it and thus I put it negative)
p0 (2, 7.06)
p1 (4.48, 3.19)
now let's find the slope:
m = (y - y0) / (x - x0)
...
m = (3.19 - 7.06) / (4.48-2) = -3.87 / 2.48 = -1.56048
...
m = -1.56
having the slope we need to calculate the equation for the tangent, basically is a rect with an already known slope (m = -1.56) that passes through an already know point (E -> (4.23, 5.76))
So we have Y = mx + b where m = -1.56, y = 5.76 and x = 4.23 so b must be
b = 5.76 - (-1.56) * 4.23 = 12.36
Now we have the complete equation for our tangent -> Y = -1.56X + 12.36
All we must do know is project the points C and D over that rect.
We need the equations for the rects CH and DI so let's calculate 'em
Let's start with CH:
We know (from the tanget's equation) that our direction vector is (1.56, 1)
We need to find a rect that passes through the point C -> (2, 7.06)
(x - 2) / 1.56 = (y - 7.06) / 1
Doing some algebra -> y = 0.64x + 5.78
We know have the equation for the rect CH we must calculate the point H.
we have to solve a linear system as follows
y = -1.56x + 12.36
y = 1.56x + 5.78
Solving this we'll find the point H (3, 7.69)
We need to do the same with the rect DI so let's do it
Our direction vector is (1.56, 1) once again
D -> (4.48, 3.19)
(x - 4.48) / 1.56 = (y -3.19) / 1
Doing some algebra -> y = 0.64x + 0.32
Lets solve the linear system
y = -1.56x + 12.36
y = 0.64x + 0.32
I (5.47, 3.82)
At this stage we already have the four points that make our Bounding box -> C, H, D , I
Just in case you don't know or rememeber how to solve a linear system on a programming language, i'll give you a little example
It's pure algebra
Let's say we have the following system
Ax + By = C
Dx + Ey = F
then
Dx = F - Ey
x = (F - Ey) / D
x = F/D - (E/D)y
replacing on the other equation
A(F/D - (E/D)y) + By = C
AF/D - (AE/D)y + By = C
(AE/D)y + By = C - AF/D
y(-AE/D + B) = C - AF/D
y = (C - AF/D) / (-AE/D + B)
= ( (CD - AF) / D ) / ( (-AE + BD) / D) )
so
y = (CD - AF) / (BD - AE)
and for x we do the same
Dx = F - Ey
Dx - F = -Ey
Ey = F - Dx
y = F/E - (D/E)x
replacing on the other equation
Ax + B(F/E - (D/E)x) = C
Ax + (BF/E - (DB/E)x) = C
Ax - (DB/E)x = C - BF/E
x (A-(DB/E)) = C - BF/E
x = (C - BF/E)/(A-(DB/E))
= ((CE - BF) / E) / ((AE-DB) / E)
x = (CE - BF) / (AE - DB)
I apologize for the extent of my answer but I meant to be as clear as possible and thus, I made it almost step by step.
In C# code:
/// <summary>
/// The input parameters describe a circular arc going _clockwise_ from E to F.
/// The output is the bounding box.
/// </summary>
public Rect BoundingBox(Point E, Point F, Point C, double radius)
{
// Put the endpoints into the bounding box:
double x1 = E.X;
double y1 = E.Y;
double x2 = x1, y2 = y1;
if (F.X < x1)
x1 = F.X;
if (F.X > x2)
x2 = F.X;
if (F.Y < y1)
y1 = F.Y;
if (F.Y > y2)
y2 = F.Y;
// Now consider the top/bottom/left/right extremities of the circle:
double thetaE = Math.Atan2(E.Y - C.Y, E.X - C.X);
double thetaF = Math.Atan2(F.Y - C.Y, F.X - C.X);
if (AnglesInClockwiseSequence(thetaE, 0/*right*/, thetaF))
{
double x = (C.X + radius);
if (x > x2)
x2 = x;
}
if (AnglesInClockwiseSequence(thetaE, Math.PI/2/*bottom*/, thetaF))
{
double y = (C.Y + radius);
if (y > y2)
y2 = y;
}
if (AnglesInClockwiseSequence(thetaE, Math.PI/*left*/, thetaF))
{
double x = (C.X - radius);
if (x < x1)
x1 = x;
}
if (AnglesInClockwiseSequence(thetaE, Math.PI*3/2/*top*/, thetaF))
{
double y = (C.Y - radius);
if (y < y1)
y1 = y;
}
return new Rect(x1, y1, x2 - x1, y2 - y1);
}
/// <summary>
/// Do these angles go in clockwise sequence?
/// </summary>
private static bool AnglesInClockwiseSequence(double x, double y, double z)
{
return AngularDiffSigned(x, y) + AngularDiffSigned(y, z) < 2*Math.PI;
}
/// <summary>
/// Returns a number between 0 and 360 degrees, as radians, representing the
/// angle required to go clockwise from 'theta1' to 'theta2'. If 'theta2' is
/// 5 degrees clockwise from 'theta1' then return 5 degrees. If it's 5 degrees
/// anticlockwise then return 360-5 degrees.
/// </summary>
public static double AngularDiffSigned(double theta1, double theta2)
{
double dif = theta2 - theta1;
while (dif >= 2 * Math.PI)
dif -= 2 * Math.PI;
while (dif <= 0)
dif += 2 * Math.PI;
return dif;
}
I tried to implement jairchu's answer, but found some problems, which I would like to share:
My coordinate system for the circle starts with 0 degrees at the right side of the circle and runs counterclockwise through the top (90deg), the left(180deg) and the bottom (270deg). The angles can be between 0 and 359,9999 deg.
The center point should not be part of the list of points
You have to distinguish between clockwise and counterclockwise arcs in order to make the list of points that lie on 0,90,180,270 deg
It is tricky to determine if the angle span includes the angle 0,90,180 or 270 deg.
public override Rect Box()
{
List<Point> potentialExtrema = new List<Point>();
potentialExtrema.Add(StartPoint);
potentialExtrema.Add(EndPoint);
if (!ClockWise)
{
if (EndAngle < StartAngle || EndAngle == 0 || StartAngle == 0 || EndAngle == 360 || StartAngle == 360)
potentialExtrema.Add(new Point(Point.X + Radius, Point.Y));
if ((StartAngle <= 90 || StartAngle > EndAngle) && EndAngle >= 90)
potentialExtrema.Add(new Point(Point.X, Point.Y + Radius));
if ((StartAngle <= 180 || StartAngle > EndAngle) && EndAngle >= 180)
potentialExtrema.Add(new Point(Point.X - Radius, Point.Y));
if ((StartAngle <= 270 || StartAngle > EndAngle) && EndAngle >= 270)
potentialExtrema.Add(new Point(Point.X, Point.Y - Radius));
}
else
{
if (StartAngle < EndAngle || EndAngle == 0 || StartAngle == 0 || EndAngle == 360 || StartAngle == 360)
potentialExtrema.Add(new Point(Point.X + Radius, Point.Y));
if ((StartAngle >= 90 || StartAngle < EndAngle) && EndAngle <= 90)
potentialExtrema.Add(new Point(Point.X, Point.Y + Radius));
if ((StartAngle >= 180 || StartAngle < EndAngle) && EndAngle <= 180)
potentialExtrema.Add(new Point(Point.X - Radius, Point.Y));
if ((StartAngle >= 270 || StartAngle < EndAngle) && EndAngle <= 270)
potentialExtrema.Add(new Point(Point.X, Point.Y - Radius));
}
double maxX = double.NegativeInfinity;
double maxY = double.NegativeInfinity;
double minX = double.PositiveInfinity;
double minY = double.PositiveInfinity;
foreach (var point in potentialExtrema)
{
if (point.X > maxX)
maxX = point.X;
if (point.Y > maxY)
maxY = point.Y;
if (point.X < minX)
minX = point.X;
if (point.Y < minY)
minY = point.Y;
}
return new Rect(minX, minY, maxX - minX, maxY - minY);
}
}
There is a more elegant solution determining wether 0,90,180 or 270 deg lie within the angle span:
public override Rect Box()
{
List<Point> potentialExtrema = new List<Point>();
potentialExtrema.Add(StartPoint);
potentialExtrema.Add(EndPoint);
if (AngleProduct(0))
potentialExtrema.Add(new Point(Point.X + Radius, Point.Y));
if (AngleProduct(90))
potentialExtrema.Add(new Point(Point.X, Point.Y + Radius));
if (AngleProduct(180))
potentialExtrema.Add(new Point(Point.X - Radius, Point.Y));
if (AngleProduct(270))
potentialExtrema.Add(new Point(Point.X, Point.Y - Radius));
double maxX = double.NegativeInfinity;
double maxY = double.NegativeInfinity;
double minX = double.PositiveInfinity;
double minY = double.PositiveInfinity;
foreach (var point in potentialExtrema)
{
if (point.X > maxX)
maxX = point.X;
if (point.Y > maxY)
maxY = point.Y;
if (point.X < minX)
minX = point.X;
if (point.Y < minY)
minY = point.Y;
}
return new Rect(minX, minY, maxX - minX, maxY - minY);
}
private bool AngleProduct(int alpha)
{
if (StartAngle == EndAngle)
if (StartAngle == alpha)
return true;
else
return false;
double prod = 0;
if (ClockWise)
prod = -1 * (alpha - StartAngle) * (EndAngle - alpha) * (EndAngle - StartAngle);
else
prod = (alpha - StartAngle) * (EndAngle - alpha) * (EndAngle - StartAngle);
if (prod >= 0)
return true;
else
return false;
}

Resources