Related
Is there any easy solution? Or has anybody an example of an implementation?
Thanks, Jonas
Lets call
a, b, c our three points,
C the circumcircle of (a, b, c)
and d an other point.
A fast way to determine if d is in C is to compute this determinant:
| ax-dx, ay-dy, (ax-dx)² + (ay-dy)² |
det = | bx-dx, by-dy, (bx-dx)² + (by-dy)² |
| cx-dx, cy-dy, (cx-dx)² + (cy-dy)² |
if a, b, c are in counter clockwise order then:
if det equal 0 then d is on C
if det > 0 then d is inside C
if det < 0 then d is outside C
here is a javascript function that does just that:
function inCircle (ax, ay, bx, by, cx, cy, dx, dy) {
let ax_ = ax-dx;
let ay_ = ay-dy;
let bx_ = bx-dx;
let by_ = by-dy;
let cx_ = cx-dx;
let cy_ = cy-dy;
return (
(ax_*ax_ + ay_*ay_) * (bx_*cy_-cx_*by_) -
(bx_*bx_ + by_*by_) * (ax_*cy_-cx_*ay_) +
(cx_*cx_ + cy_*cy_) * (ax_*by_-bx_*ay_)
) > 0;
}
You might also need to check if your points are in counter clockwise order:
function ccw (ax, ay, bx, by, cx, cy) {
return (bx - ax)*(cy - ay)-(cx - ax)*(by - ay) > 0;
}
I didn't place the ccw check inside the inCircle function because you shouldn't check it every time.
This process doesn't take any divisions or square root operation.
You can see the code in action there: https://titouant.github.io/testTriangle/
And the source there: https://github.com/TitouanT/testTriangle
(In case you are interested in a non-obvious/"crazy" kind of solution.)
One equivalent property of Delaunay triangulation is as follows: if you build a circumcircle of any triangle in the triangulation, it is guaranteed not to contain any other vertices of the triangulation.
Another equivalent property of Delaunay triangulation is: it maximizes the minimal triangle angle (i.e. maximizes it among all triangulations on the same set of points).
This suggests an algorithm for your test:
Consider triangle ABC built on the original 3 points.
If the test point P lies inside the triangle it is definitely inside the circle
If the test point P belongs to one of the "corner" regions (see the shaded regions in the picture below), it is definitely outside the circle
Otherwise (let's say P lies in region 1) consider two triangulations of quadrilateral ABCP: the original one contains the original triangle (red diagonal), and the alternate one with "flipped" diagonal (blue diagonal)
Determine which one if this triangulations is a Delaunay triangulation by testing the "flip" condition, e.g. by comparing α = min(∠1,∠4,∠5,∠8) vs. β = min(∠2,∠3,∠6,∠7).
If the original triangulation is a Delaunay triangulation (α > β), P lies outside the circle. If the alternate triangulation is a Delaunay triangulation (α < β), P lies inside the circle.
Done.
(Revisiting this answer after a while.)
This solution might not be as "crazy" as it might appear at the first sight. Note that in order to compare angles at steps 5 and 6 there's no need to calculate the actual angle values. It is sufficient to know their cosines (i.e. there's no need to involve trigonometric functions).
A C++ version of the code
#include <cmath>
#include <array>
#include <algorithm>
struct pnt_t
{
int x, y;
pnt_t ccw90() const
{ return { -y, x }; }
double length() const
{ return std::hypot(x, y); }
pnt_t &operator -=(const pnt_t &rhs)
{
x -= rhs.x;
y -= rhs.y;
return *this;
}
friend pnt_t operator -(const pnt_t &lhs, const pnt_t &rhs)
{ return pnt_t(lhs) -= rhs; }
friend int operator *(const pnt_t &lhs, const pnt_t &rhs)
{ return lhs.x * rhs.x + lhs.y * rhs.y; }
};
int side(const pnt_t &a, const pnt_t &b, const pnt_t &p)
{
int cp = (b - a).ccw90() * (p - a);
return (cp > 0) - (cp < 0);
}
void make_ccw(std::array<pnt_t, 3> &t)
{
if (side(t[0], t[1], t[2]) < 0)
std::swap(t[0], t[1]);
}
double ncos(pnt_t a, const pnt_t &o, pnt_t b)
{
a -= o;
b -= o;
return -(a * b) / (a.length() * b.length());
}
bool inside_circle(std::array<pnt_t, 3> t, const pnt_t &p)
{
make_ccw(t);
std::array<int, 3> s =
{ side(t[0], t[1], p), side(t[1], t[2], p), side(t[2], t[0], p) };
unsigned outside = std::count(std::begin(s), std::end(s), -1);
if (outside != 1)
return outside == 0;
while (s[0] >= 0)
{
std::rotate(std::begin(t), std::begin(t) + 1, std::end(t));
std::rotate(std::begin(s), std::begin(s) + 1, std::end(s));
}
double
min_org = std::min({
ncos(t[0], t[1], t[2]), ncos(t[2], t[0], t[1]),
ncos(t[1], t[0], p), ncos(p, t[1], t[0]) }),
min_alt = std::min({
ncos(t[1], t[2], p), ncos(p, t[2], t[0]),
ncos(t[0], p, t[2]), ncos(t[2], p, t[1]) });
return min_org <= min_alt;
}
and a couple of tests with arbitrarily chosen triangles and a large number of random points
Of course, the whole thing can be easily reformulated without even mentioning Delaunay triangulations. Starting from step 4 this solution is based in the property of the opposite angles of cyclic quadrilateral, which must sum to 180°.
In this Math SE post of mine I included an equation which checks if four points are cocircular by computing a 4×4 determinant. By turning that equation into an inequality you can check for insideness.
If you want to know which direction the inequality has to go, conisder the case of a point very far away. In this case, the x²+y² term will dominate all other terms. So you can simply assume that for the point in question, this term is one while the three others are zero. Then pick the sign of your inequality so this value does not satisfy it, since this point is definitely outside but you want to characterize inside.
If numeric precision is an issue, this page by Prof. Shewchuk describes how to obtain consistent predicates for points expressed using regular double precision floating point numbers.
Given 3 points (x1,y1),(x2,y2),(x3,y3) and the point you want to check is inside the circle defined by the above 3 points (x,y) you can do something like
/**
*
* #param x coordinate of point want to check if inside
* #param y coordinate of point want to check if inside
* #param cx center x
* #param cy center y
* #param r radius of circle
* #return whether (x,y) is inside circle
*/
static boolean g(double x,double y,double cx,double cy,double r){
return Math.sqrt((x-cx)*(x-cx)+(y-cy)*(y-cy))<r;
}
// check if (x,y) is inside circle defined by (x1,y1),(x2,y2),(x3,y3)
static boolean isInside(double x,double y,double x1,double y1,double x2,double y2,double x3,double y3){
double m1 = (x1-x2)/(y2-y1);
double m2 = (x1-x3)/(y3-y1);
double b1 = ((y1+y2)/2) - m1*(x1+x2)/2;
double b2 = ((y1+y3)/2) - m2*(x1+x3)/2;
double xx = (b2-b1)/(m1-m2);
double yy = m1*xx + b1;
return g(x,y,xx,yy,Math.sqrt((xx-x1)*(xx-x1)+(yy-y1)*(yy-y1)));
}
public static void main(String[] args) {
// if (0,1) is inside the circle defined by (0,0),(0,2),(1,1)
System.out.println(isInside(0,1,0,0,0,2,1,1));
}
The method for getting an expression for the center of circle from 3 points goes from finding the intersection of the 2 perpendicular bisectors of 2 line segments, above I chose (x1,y1)-(x2,y2) and (x1,y1)-(x3,y3). Since you know a point on each perpendicular bisector, namely (x1+x2)/2 and (x1+x3)/2, and since you also know the slope of each perpendicular bisector, namely (x1-x2)/(y2-y1) and (x1-x3)/(y3-y1) from the above 2 line segments respectively, you can solve for the (x,y) where they intersect.
I was asked this question in an interview and I couldn't solve it.
I would be really grateful if you could help me solve it.
The problem is as follows:-
You are given a rectangular region whose left and bottom-most co-ordinate is (0,0) and right-most and top-most co-ordinate is (x,y).
There are n circles with given center co-ordinates that exist inside the region all having the same radius 'r'.
You are currently standing at (0,0) and want to reach (x,y) without touching any circle.
You have to tell if it is possible or not.
He told me that you can move between 2 points freely and it is not necessary to move along the x or y-axis.
The solution I thought of involved taking a matrix of x*y dimension and for each circle, mark the points which lie inside it or touch it.
After that apply BFS starting from (0,0) to check if we can reach (x,y).
He told me BFS will be wrong which I couldn't figure out why.
I had assumed that circles are having integer radius and have integer co-ordinates.
He also asked me to solve the question without these assumptions.
I couldn't. When asked, he told me it's a standard problem and I should be able to find it on google.
I couldn't, again. Please help!
The interviewer is wrong on the part that BFS cannot be used. Whenever you step into a cell, check that if the cell is within a circle or not, by checking the cell's distance from every other circle centers available to you, if it is within dist<=R then that cell can't be reached from the present particular cell. I solved the similar question present in interviewbit -
https://www.interviewbit.com/problems/valid-path/
The code is simple -
public class Solution {
private class Pair
{
int x ; int y ;
}
ArrayList<Integer> xindex ; ArrayList<Integer> yindex ; int R ;int len ;
public String solve(int x, int y, int n, int r, ArrayList<Integer> xi, ArrayList<Integer> yi) {
int dp[][] = new int[x+1][y+1] ;
len = xi.size() ;
for(int i=0;i<=x;i++)
{
for(int j=0;j<=y;j++)
dp[i][j] = -1 ;
}
xindex = xi ; yindex = yi ;
dp[0][0] = 1 ; R = r*r ;
LinkedList<Pair> q = new LinkedList<Pair>() ;
Pair obj = new Pair() ;
obj.x = 0 ; obj.y = 0 ;
q.add(obj) ;
int arr1[] = {1,1,1,0,-1,-1,-1,0} ;
int arr2[] = {-1,0,1,1,1,0,-1,-1} ;
while(q.size()!=0)
{
Pair temp = q.poll() ;
int x1 = temp.x ;
int x2 = temp.y ;
for(int i=0;i<8;i++)
{
int t1 = x1+arr1[i] ; int t2 = x2+arr2[i] ;
if((t1>=0)&&(t1<=x)&&(t2>=0)&&(t2<=y))
{
if(dp[t1][t2]==-1)
{
boolean res = isValidIndex(t1,t2) ;
if(res==false)
dp[t1][t2] = 2 ;
else
{
dp[t1][t2] = 1 ;
Pair t = new Pair() ;
t.x = t1 ;
t.y = t2 ;
q.add(t) ;
}
}
}
}
if(dp[x][y]!=-1)
break ;
}
if(dp[x][y]==1)
return "YES" ;
return "NO" ;
}
public boolean isValidIndex(int x,int y)
{
for(int i=0;i<len;i++)
{
int x1 = xindex.get(i) ; int x2 = yindex.get(i) ;
if((x==x1)&&(y==x2))
return false ;
int n = (x1-x)*(x1-x) + (x2-y)*(x2-y) ;
if(n<=R)
return false ;
}
return true ;
}
}
If two circles are touching, draw a line segment between their centers. If a circle touches an edge of the rectangle, joint its center to its projection on the closest side. Then discard the circles. This doesn't modify the connexity of the obstacles and you have turned the problem to the more famliar one of a planar straight line subdivision.
An approach could be by decomposing the domain in slabs, i.e. drawing horizontal lines through every center to partition the plane in trapezoids. Then by a seed filling approach, one can determine the starting slab and extend accessibility to the slabs that have a common horizontal side with it, until either a closed region is filled or the exit slab is reached.
Below, an intermediate step of seed filling from the top-left slab.
If the circle centers are on a regular grid, standard seed filling can do.
I think 2 circles can only block the path if the distance between their centers is less than 2r. So it should be enough to build "islands" of overlapping circles and check if any island blocks the path from 0 to (x,y), i.e. if any circles in it intersect rectangle borders in such a way that a straight line between those intersection points would block the path.
Lazy approach:
Visit each ball in turn
If it does not touch any other ball, scrap it.
If it touches another one, add it and the other one to your favorite node graph system and create a connection between them (do some housekeeping to avoid doublettes).
Create the extra balls A, B, C, D
Create extra connections (lines in the pic) between A and all balls that touch the left edge, between B and all balls that touch the top edge, etc.
Ask your A* if it's willing to navigate you from A to C / A to D / B to C / B to D.
If yes to any of above, then the answer to the Q is no. Quick and dirty :-).
Best approach to solve this problem is using a dfs search. First lets consider some of the basic cases like if there exist a circle containing either of the two points (i.e 0,0 or x,y) but not both then answer is "NO" and if there exist a circle containing both of the points then we can remove it from our list of circles. Now we are left with circles that don't contain either of the two points now make a graph using centers of these circles as a vertex and join any two vertex if the distance between them is less than or equal to 2*R and also maintain an array of mask for all circles, mask store the sides cut by a particular circle i.e if we number left vertical side as 0 top horizontal side as 1 right vertical side as 2 and bottom horizontal side as 3 then in mask corresponding to a particular circle those bits would be active which correspond to sides cut by that circle.
Now just perform a dfs and see whether there exist a path in graph which shows whether it is possible to move from, side 0 to 2 or side 0 to 3 or side 1 to 2 or side 1 to 2, using edges of graph (we use mask to check that). If such a path exists then answer is "NO" else answer is always "YES".
I did this question in the exact manner as suggested by Anton. Used DSU to make so called islands. And then identified the following conditions : TB,LR,TR,LB (T: Top, B:Bottom, L:Left, R:Right ) intersections. If any of the islands made these intersections , they are sure to block the path and the answer will be "NO".
The question can be found at Interview Bit:
https://www.interviewbit.com/problems/valid-path/
And the corresponding solution is given here:
http://ideone.com/19iSOn
#include<bits/stdc++.h>
#include<unordered_set>
using namespace std;
class Solution{
public:
string solve(int A, int B, int C, int D, vector<int> &E, vector<int> &F);
};
#define pii pair<int,int>
int par[1000+5];
int rnk[1000+5];
bool vis[1000+5];
void initialise(){
for(int i=0;i<=1000;i++){
par[i]=i;
rnk[i]=1;
vis[i]=false;
}
}
int findPar(int node){
if(par[node]==node)return node;
return par[node]=findPar(par[node]);
}
void makeUnion(int a,int b){
int parA=findPar(a);
int parB=findPar(b);
if(parA==parB)return;
if(rnk[parA]<rnk[parB])par[parB]=parA;
else if(rnk[parB]<rnk[parA])par[parA]=parB;
else{
rnk[parA]++;
par[parB]=parA;
}
}
bool findBlockage(int root,int X,int Y,int N,int R,vector<pair<int,pii>>vec){
int top=0;
int bottom=INT_MAX;
int left=INT_MAX;
int right=0;
for(int i=0;i<N;i++){
if(par[vec[i].first]==root){
int x=vec[i].second.first;
int y=vec[i].second.second;
top=max(top,y+R);
bottom=min(bottom,y-R);
left=min(left,x-R);
right=max(right,x+R);
}
}
if(top>=Y and bottom<=0)return true;
if(right>=X and left<=0)return true;
if(top>=Y and right>=X)return true;
if(left<=0 and bottom<=0)return true;
return false;
}
string Solution::solve(int X, int Y, int N, int R, vector<int> &E, vector<int> &F) {
vector<pair<int,pii>> vec;
int id=0;
for(int i=0;i<N;i++){
vec.push_back({id,{E[i],F[i]}});
id++;
}
initialise();
for(int i=0;i<N;i++){
for(int j=i;j<N;j++){
if(i==j)continue;
int x1=vec[i].second.first;
int x2=vec[j].second.first;
int y1=vec[i].second.second;
int y2=vec[j].second.second;
if(((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)) <= (4*R*R)){
makeUnion(vec[i].first,vec[j].first);
}
}
}
for(int i=0;i<N;i++){
if(!vis[par[vec[i].first]]){
vis[par[vec[i].first]]=1;
bool ret = findBlockage(par[vec[i].first],X,Y,N,R,vec);
if(ret)return "NO";
}
}
return "YES";
}
int main(){
int n,x,y,r;
cin>>x>>y>>n>>r;
vector<int>X(n);
vector<int>Y(n);
for(int i=0;i<n;i++)cin>>X[i];
for(int i=0;i<n;i++)cin>>Y[i];
Solution sol;
cout<<sol.solve(n,x,y,r,X,Y)<<endl;
}
A recursive solution to this problem (find path from [0,0] to [x,y] with circles as obstructions), using simple DP concepts:
public class Solution {
public long radiusSquare;
public int xDest, yDest;
boolean vis[][];
public final static int[] xDelta = new int[]{0, 1, -1, 0, 1, -1, -1, 1};
public final static int[] yDelta = new int[]{1, 0, 0, -1, 1, -1, 1, -1};
public String solve(int x, int y, int radius, ArrayList<Integer> X, ArrayList<Integer> Y) {
xDest = x; yDest = y;
radiusSquare = radius*radius;
int dp[][] = new int[x+1][y+1]; // = 0 (not visited), = 1 (a valid point), = 2 (invalid)
vis = new boolean[x+1][y+1];
if (recur(0, 0, X, Y, dp)) return "YES";
return "NO";
}
public boolean recur(int xi, int yi, ArrayList<Integer> X,
ArrayList<Integer> Y, int dp[][]){
if (xi == xDest && yi == yDest) return true;
if(vis[xi][yi]) return false; // already processed coordinate
vis[xi][yi] = true;
for (int i =0; i < xDelta.length; i++){
int xArg = xi+xDelta[i];
int yArg = yi+yDelta[i];
if (validCoordinates(xArg, yArg, X, Y, dp) && recur(xArg, yArg, X, Y, dp))
return true;
}
return false;
}
public boolean validCoordinates(int x, int y, ArrayList<Integer> X, ArrayList<Integer> Y, int dp[][]){
if (x < 0 || y < 0 || x > xDest || y > yDest) return false;
if (dp[x][y] != 0){
if (dp[x][y] == 1) return true; // valid coord
if (dp[x][y] == 2) return false;
}
for (int i = 0; i < X.size(); i++){ // loop through each circle.
long sumOfSquare = ((x-X.get(i))*(x-X.get(i))) + ((y-Y.get(i))*(y-Y.get(i)));
if (sumOfSquare <= radiusSquare){
dp[x][y] = 2; // comes on or inside circle
return false;
}
}
dp[x][y] = 1;
return true;
}
}
In a nutshell: I want to do a non-approximate version of Bresenham's line algorithm, but for a rectangle rather than a line, and whose points aren't necessarily aligned to the grid.
Given a square grid, and a rectangle comprising four non-grid-aligned points, I want to find a list of all grid squares that are covered, partially or completely, by the rectangle.
Bresenham's line algorithm is approximate – not all partially covered squares are identified. I'm looking for a "perfect" algorithm, that has no false positives or negatives.
It's an old question, but I have solved this issue (C++)
https://github.com/feelinfine/tracer
Maybe it will be usefull for someone
(sorry for my poor english)
Single line tracing
template <typename PointType>
std::set<V2i> trace_line(const PointType& _start_point, const PointType& _end_point, size_t _cell_size)
{
auto point_to_grid_fnc = [_cell_size](const auto& _point)
{
return V2i(std::floor((double)_point.x / _cell_size), std::floor((double)_point.y / _cell_size));
};
V2i start_cell = point_to_grid_fnc(_start_point);
V2i last_cell = point_to_grid_fnc(_end_point);
PointType direction = _end_point - _start_point;
//Moving direction (cells)
int step_x = (direction.x >= 0) ? 1 : -1;
int step_y = (direction.y >= 0) ? 1 : -1;
//Normalize vector
double hypot = std::hypot(direction.x, direction.y);
V2d norm_direction(direction.x / hypot, direction.y / hypot);
//Distance to the nearest square side
double near_x = (step_x >= 0) ? (start_cell.x + 1)*_cell_size - _start_point.x : _start_point.x - (start_cell.x*_cell_size);
double near_y = (step_y >= 0) ? (start_cell.y + 1)*_cell_size - _start_point.y : _start_point.y - (start_cell.y*_cell_size);
//How far along the ray we must move to cross the first vertical (ray_step_to_vside) / or horizontal (ray_step_to_hside) grid line
double ray_step_to_vside = (norm_direction.x != 0) ? near_x / norm_direction.x : std::numeric_limits<double>::max();
double ray_step_to_hside = (norm_direction.y != 0) ? near_y / norm_direction.y : std::numeric_limits<double>::max();
//How far along the ray we must move for horizontal (dx)/ or vertical (dy) component of such movement to equal the cell size
double dx = (norm_direction.x != 0) ? _cell_size / norm_direction.x : std::numeric_limits<double>::max();
double dy = (norm_direction.y != 0) ? _cell_size / norm_direction.y : std::numeric_limits<double>::max();
//Tracing loop
std::set<V2i> cells;
cells.insert(start_cell);
V2i current_cell = start_cell;
size_t grid_bound_x = std::abs(last_cell.x - start_cell.x);
size_t grid_bound_y = std::abs(last_cell.y - start_cell.y);
size_t counter = 0;
while (counter != (grid_bound_x + grid_bound_y))
{
if (std::abs(ray_step_to_vside) < std::abs(ray_step_to_hside))
{
ray_step_to_vside = ray_step_to_vside + dx; //to the next vertical grid line
current_cell.x = current_cell.x + step_x;
}
else
{
ray_step_to_hside = ray_step_to_hside + dy;//to the next horizontal grid line
current_cell.y = current_cell.y + step_y;
}
++counter;
cells.insert(current_cell);
};
return cells;
}
Get all cells
template <typename Container>
std::set<V2i> pick_cells(Container&& _points, size_t _cell_size)
{
if (_points.size() < 2 || _cell_size <= 0)
return std::set<V2i>();
Container points = std::forward<Container>(_points);
auto add_to_set = [](auto& _set, const auto& _to_append)
{
_set.insert(std::cbegin(_to_append), std::cend(_to_append));
};
//Outline
std::set<V2i> cells;
/*
for (auto it = std::begin(_points); it != std::prev(std::end(_points)); ++it)
add_to_set(cells, trace_line(*it, *std::next(it), _cell_size));
add_to_set(cells, trace_line(_points.back(), _points.front(), _cell_size));
*/
//Maybe this code works faster
std::vector<std::future<std::set<V2i> > > results;
using PointType = decltype(points.cbegin())::value_type;
for (auto it = points.cbegin(); it != std::prev(points.cend()); ++it)
results.push_back(std::async(trace_line<PointType>, *it, *std::next(it), _cell_size));
results.push_back(std::async(trace_line<PointType>, points.back(), points.front(), _cell_size));
for (auto& it : results)
add_to_set(cells, it.get());
//Inner
std::set<V2i> to_add;
int last_x = cells.begin()->x;
int counter = cells.begin()->y;
for (auto& it : cells)
{
if (last_x != it.x)
{
counter = it.y;
last_x = it.x;
}
if (it.y > counter)
{
for (int i = counter; i < it.y; ++i)
to_add.insert(V2i(it.x, i));
}
++counter;
}
add_to_set(cells, to_add);
return cells;
}
Types
template <typename _T>
struct V2
{
_T x, y;
V2(_T _x = 0, _T _y = 0) : x(_x), y(_y)
{
};
V2 operator-(const V2& _rhs) const
{
return V2(x - _rhs.x, y - _rhs.y);
}
bool operator==(const V2& _rhs) const
{
return (x == _rhs.x) && (y == _rhs.y);
}
//for std::set sorting
bool operator<(const V2& _rhs) const
{
return (x == _rhs.x) ? (y < _rhs.y) : (x < _rhs.x);
}
};
using V2d = V2<double>;
using V2i = V2<int>;
Usage
std::vector<V2d> points = { {200, 200}, {400, 400}, {500,100} };
size_t cell_size = 30;
auto cells = pick_cells(points, cell_size);
for (auto& it : cells)
... //do something with cells
You can use a scanline approach. The rectangle is a closed convex polygon, so it is sufficient to store the leftmost and rightmost pixel for each horizontal scanline. (And the top and bottom scanlines, too.)
The Bresenham algorithm tries to draw a thin, visually pleasing line without adjacent cells in the smaller dimension. We need an algorithm that visits each cell that the edges of the polygon pass through. The basic idea is to find the starting cell (x, y) for each edge and then to adjust x whenever the edge intersects a vertical border and to adjust y when it intersects a horizontal border.
We can represent the intersections by means of a normalised coordinate s that travels along the edge and that is 0.0 at the first node n1 and 1.0 at the second node n2.
var x = Math.floor(n1.x / cellsize);
var y = Math.floor(n1.y / cellsize);
var s = 0;
The vertical insersections can the be represented as equidistant steps of with dsx from an initial sx.
var dx = n2.x - n1.x;
var sx = 10; // default value > 1.0
// first intersection
if (dx < 0) sx = (cellsize * x - n1.x) / dx;
if (dx > 0) sx = (cellsize * (x + 1) - n1.x) / dx;
var dsx = (dx != 0) ? grid / Math.abs(dx) : 0;
Likewise for the horizontal intersecions. A default value greater than 1.0 catches the cases of horizontal and vertical lines. Add the first point to the scanline data:
add(scan, x, y);
Then we can visit the next adjacent cell by looking at the next intersection with the smallest s.
while (sx <= 1 || sy <= 1) {
if (sx < sy) {
sx += dsx;
if (dx > 0) x++; else x--;
} else {
sy += dsy;
if (dy > 0) y++; else y--;
}
add(scan, x, y);
}
Do this for all four edges and with the same scanline data. Then fill all cells:
for (var y in scan) {
var x = scan[y].min;
var xend = scan[y].max + 1;
while (x < xend) {
// do something with cell (x, y)
x++;
}
}
(I have only skimmed the links MBo provided. It seems that the approach presented in that paper is essentially the same as mine. If so, please excuse the redundant answer, but after working this out I thought I could as well post it.)
This is sub-optimal but might give a general idea.
First off treat the special case of the rectangle being aligned horizontally or vertically separately. This is pretty easy to test for and make the rest simpler.
You can represent the rectangle as a set of 4 inequalities a1 x + b1 y >= c1 a1 x + b1 y <= c2 a3 x + b3 y >= c3 a3 x + b3 y <= c4 as the edges of the rectangles are parallel some of the constants are the same. You also have (up to a multiple) a3=b1 and b3=-a1. You can multiply each inequality by a common factor so you are working with integers.
Now consider each scan line with a fixed value of y.
For each value of y find the four points where the lines intersect the scan line. That is find the solution with each line above. A little bit of logic will find the minimum and maximum values of x. Plot all pixels between these values.
You condition that you want all partially covered squares makes things a little trickier. You can solve this by considering two adjacent scan lines. You want to plot the points between the minimum x for both lines and the maximum for the both lines. If say
a1 x+b1 y>=c is the inequality for the bottom left line in the figure. You want the find the largest x such that a1 x + b1 y < c this will be floor((c-b1 y)/a1) call this minx(y) also find minx(y+1) and the left hand point will be the minimum of these two values.
There is many easy optimisation you can find the y-values of the top and bottom corners reducing the range of y-values to test. You should only need to test two side. For each end point of each line there is one multiplication, one subtraction and one division. The division is the slowest part I think about 4 time slower than other ops. You might be able to remove this with a Bresenham or DDA algorithms others have mentioned.
There is method of Amanatides and Woo to enumerate all intersected cells
A Fast Voxel Traversal Algorithm for Ray Tracing.
Here is practical implementation.
As side effect for you - you'll get points of intersection with grid lines - it may be useful if you need areas of partially covered cells (for antialiasing etc).
void line()
{
int x1 = 10, y1 = 10, x2 = 300, y2 = 500 , x, y;
int dx, dy, //deltas
e; // decision parameter
glClear(GL_COLOR_BUFFER_BIT);
glColor3f( 1 ,0, 0);
setPixel(x1, y1); //plot first point
// difference between starting and ending points
dx = x2 - x1;
dy = y2 - y1;
e = 2 * dy - dx;
x = x1; y = y1;
for(int k = 0; k < dx - 1; ++k)
{
if(e < 0)
{
//next pixel: (x+1, y)
e = e + 2*dy;
}
else
{
//next pixel: (x+1, y+1)
e = e + 2*dy - 2*dx;
++y;
}
++x;
setPixel(x, y);
}
glFlush();
}
Where does the e = 2*dy - dx come from? Why do we increase it by 2*dy or 2*dy - 2*dx?
Bresenham's algorithm uses only integer arithmetic. The key idea is to minimize the calculations for incremental evaluation of the line equation.
The algorithm is really simple. Let's start with the line equation
f(x) = y = a*x +b
(and assume 0 <= a < 1 for now).
When we go one pixel to the right, we get:
f(x+1) = a * (x+1) + b = f(x) + a
But both a and y will not be integers for the typical line.
So let's just introduce an "error". We always go to the right neighbor. In doing so, we make an error of a by not going up. If our error is above half a pixel (0.5), we go up (and hence decrease the error value by a pixel again)
float e=a;
float y=y1;
int x=x1;
while(x<=x2) {
SetPixel(x,y);
x++;
if (e > 0.5) {
y++;
e=e+a-1;
} else {
e=e+a;
}
}
(Note that we already set the error e to a initially and not to zero, because we always make the decision after the pixel is drawn, and we don't need to check the condition before drawing the very first pixel because that one is always exactly on the line.)
Now, we have come close. But there are two things which prevent us from using integers: the 0.5 and a which is dy/dx. But: we can scale the error value (and the condition) by an arbitray factor, without changing anything. Think about it: we've measured the error in pixels so far (because that seems intuitive at first), but this algorithm could use any arbitrary unit for the error value - half pixels, double pixels, pi pixels.
So let's just scale it by 2*dx to get rid of both fractions in the formula above! (In a way, they key trick here is that the "unit" in which we measure the error value is just not constant in the algorithm, but a function of the line).
int e=2*dy;
int y=y1;
int x=x1;
while(x<=x2) {
SetPixel(x,y);
x++;
if (e > dx) {
y++;
e=e+2*dy - 2*dx;
} else {
e=e+2*dy;
}
}
Now, we have what we want: only integers.
(One thing to note here, though: by going from float to int, we automatically "snap-in" the line's endpoints to integer coordinates - having integer endpoints is some precondition for (and limitation of) the Bresenham algorithm).
There is one additional trick: the condition contains a variable. It would be even more efficient, if we would test against a constant, and ideally against zero (since branching depending just on the sign or zero flags saves us a compare operation). And we can achive this, by just shifiting our error values. In the same way as before, not only the scale of the error value cane be chosen arbitrarily, but also origin.
Since we test for e > dx currently, shifting the error by -dx will allow us to test against 0 (and 0 now means what dx meant before, namely 0.5 pixels). This shift only affects the initial value of e, and the condition, all the increments stay the same as before:
int e=2*dy-dx;
int y=y1;
int x=x1;
while(x<=x2) {
SetPixel(x,y);
x++;
if (e > 0) {
y++;
e=e+2*dy - 2*dx;
} else {
e=e+2*dy;
}
}
Voila, the 2*dy-dx term has suddenly emerged... ;)
The term 2dy-dx comes after we fill xk =yk=0 in the formula (2dy•xk-2dx•yk+2dy+(2b-1)) because for the first parameter we assume the starting point of line lies at origin i.e (0,0).
And b is constant so it is ignored.
Try it by yourself.
I am trying to solve simple task, but I am not finding any elegant solution.
I basically solving intersection of two circular sectors.
Each sector is given by 2 angles (from atan2 func) within (-pi, pi] range.
Each selector occupy maximum angle of 179.999. So it can be tell for every two angles where the circular sector is.
The return value should describe mutual intersection based on following:
value <1 if one angle is contained by second one (value represents how much space occupy percentually)
value >1 if first angle (the dotted one) is outside the other one, value represents how much of dotted angle is out of the other one
basic cases and some examples are on image bellow
the problem is that there are so many cases which should be handled and I am looking for some elegant way to solve it.
I can compare two angles only when they are on the right side of unit circle (cos>0) because on the left side, angle numerically bigger is graphically lower. I tried use some projection on the right half:
if(x not in <-pi/2, pi/2>)
{
c = getSign(x)*pi/2;
x = c - (x - c);
}
but there is a problem with sectors which occupy part of both halves of unit circle...
There are so many cases... Does somebody know how to solve this elegantly?
(I use c++, but any hint or pseudocode is fine)
You can do the following:
normalize each sector to the form (s_start, s_end) where s_start is in (-pi,pi] and s_end in [s_start,s_start+pi).
sort (swap) the sectors such that s0_start < s1_start
now we have only 3 cases (a, b1, b2):
a) s1_start <= s0_end: intersection, s1_start inside s0
b) s1_start > s0_end:
b1) s0_start + 2*pi <= s1_end: intersection, (s0_start + 2*pi) inside s1
b2) s0_start + 2*pi > s1_end: no intersection
Thus we get the following code:
const double PI = 2.*acos(0.);
struct TSector { double a0, a1; };
// normalized range for angle
bool isNormalized(double a)
{ return -PI < a && a <= PI; }
// special normal form for sector
bool isNormalized(TSector const& s)
{ return isNormalized(s.a0) && s.a0 <= s.a1 && s.a1 < s.a0+PI; }
// normalize a sector to the special form:
// * -PI < a0 <= PI
// * a0 < a1 < a0+PI
void normalize(TSector& s)
{
assert(isNormalized(s.a0) && isNormalized(s.a1));
// choose a representation of s.a1 s.t. s.a0 < s.a1 < s.a0+2*PI
double a1_bigger = (s.a0 <= s.a1) ? s.a1 : s.a1+2*PI;
if (a1_bigger >= s.a0+PI)
std::swap(s.a0, s.a1);
if (s.a1 < s.a0)
s.a1 += 2*PI;
assert(isNormalized(s));
}
bool intersectionNormalized(TSector const& s0, TSector const& s1,
TSector& intersection)
{
assert(isNormalized(s0) && isNormalized(s1) && s0.a0 <= s1.a0);
bool isIntersecting = false;
if (s1.a0 <= s0.a1) // s1.a0 inside s0 ?
{
isIntersecting = true;
intersection.a0 = s1.a0;
intersection.a1 = std::min(s0.a1, s1.a1);
}
else if (s0.a0+2*PI <= s1.a1) // (s0.a0+2*PI) inside s1 ?
{
isIntersecting = true;
intersection.a0 = s0.a0;
intersection.a1 = std::min(s0.a1, s1.a1-2*PI);
}
assert(!isIntersecting || isNormalized(intersection));
return isIntersecting;
}
main()
{
TSector s0, s1;
s0.a0 = ...
normalize(s0);
normalize(s1);
if (s1.a0 < s0.a0)
std::swap(s0, s1);
TSection intersection;
bool isIntersection = intersectionNormalized(s0, s1, intersection);
}