I have 2d map and I want to check if line collides with any element. I need a function that can tell me if the line intersect any object on its way.
Take a look:
Red lines are not correct (function should return false), and green are (return true).
My collision map is map of boolean with 1 for wall and 0 for empty space.
How to this? I've read that I need to check if the line intersect any wall, but i have completely no idea how to do this on 2d map.
Thanx for any replies.
It depends on how your walls are represented.
If they are rectangle, then look for the line/segment intersection between your line and the 4 segments representing the rectangle.
If they are pixels, you can use the bresenham line algorithm to see if the pixels on the line are on these blocks.
If your walls are represented as line-segments you could test for intersection of line-segments as Paul Bourke describes: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
If your walls are represented as polygons you could clip your test line against the wall polygon and see if the clipping result is non-empty as Paul Bourke describes: http://local.wasp.uwa.edu.au/~pbourke/geometry/cliplinetopoly/
So, I imagined that your cells are squares... let say they're unit squares. So, if I have any coordinate, for a point like , which are floats or doubles, they are in cell .
you need to know in which cells are the endpoints of the line
walk straight from one endpoint to the other and for each cell, test if it's a wall or not
return false when a wall is found, true otherwise.
To walk from one endpoint to the other, you need to compute the delta of each axis (call delta_x and delta_y, those values are 'int' because we're talking cells here), i.e. the number of cells it goes on the vertical and on the horizontal. You take the largest of the two. You will use that largest value for your loop. Let say it's this value is D = max(delta_x, delta_y) and XX and YY are the coordinate of the cell of one endpoint.
float step = 1.0f / D;
float current_location = 0.0;
for (int i = 0; i <= D; ++i, current_location += step)
{
int cur_x = XX + current_location * delta_x;
int cur_y = YY + current_location * delta_y;
if (intersect_wall(cur_x, cur_y))
return false;
}
return true;
That's it... adapt this to your functions.
Related
I want to find a best way because all coordinates are integer values and polygons consist of horizontal and vertical lines only. I think there may be a simple and fast method to do this.
From an asymptotic complexity point of view, a rectilinear polygon is not really simpler to process than a general one: O(N) without preprocessing, and O(Log N) after O(N Log N) preprocessing (but using a complicated procedure).
For the case of no preprocessing, the procedure is simple: consider every vertical side in turn and count those that cross the horizontal half-line from the given point (+1 upward, -1 downward). The point is inside if the final count is nonzero.
The status of points on the outline is application-dependent.
For rectilinear poygons with not too large integer coordinates, you can anyway do a little better, by "compressing" them. By two independent sorts on X and Y, you can obtain a mapping from X (or Y) to integer indexes in range [0,N). This gives the shrunk polygon below, of size NxN.
Now you can embed the polygon in an image and preprocess to label the pixels as inside/outside (by seed-filling). After filling two lookup-tables for coordinate conversion, you can obtain the status of any point in constant time O(1).
This will take O(N²+M) preprocessing time and storage, where M is the range of X and Y values.
Consider any polygon, not necessary convex, formed only with horizontal and vetical lines:
Take a point (I've drawn A,B,C,D) and draw horizontal and vertical lines passing through the point.
Let's take point A. You see the horizontal line through it crosses four (vertical) segments. Note one segment is at left and the others are at right.
For point B its horizontal line crosses also four segments, but two at left and two at right.
The conditions that a point must fulfill to be inside a polygon are:
At least one segment is horizontaly crossed at left of the point.
At least one segment is horizontaly crossed at right of the point.
Both numbers of crosses left, right must be odd.
Same three conditions for vertical lines.
So, in pseudocode it goes like this:
let nL = 0, nR = 0 //left/right counters
let nA = 0, nU = 0 //above/under counters
for each segment s(sx1,sy1, sx2, sy2) in polygon
if point is on segment
return true //or false, your choice
else if segment is vertical and pointY is inside of (sy1,sy2)
if pointX > min(sx1,sx2)
nL = nL + 1
else
nR = nR + 1
else if segment is horizontal and pointX is inside of (sx1,sx2)
if pointY > min(sy1,sy2)
nU = nU + 1
else
nA = nA + 1
//Check conditions
if nL > 0 and nR > 0 and nL is odd and nR is odd
return true
if nA > 0 and nU > 0 and nA is odd and nU is odd
return true
return false
I'm trying to program a simulation. Originally I'd randomly create points like so...
for (int c = 0; c < number; c++){
for(int d = 0; d < 3; d++){
coordinate[c][d] = randomrange(low, high);
}
}
Where randomrange() is an arbitrary range randomizer, number is the amount of created points, and d represents the x,y,z coordinate. It works, however I want to take things further. How would I define a known shape? Say I want 80 points on a circle's circumference, or 500 that form the edges of a cube. I can explain well on paper, but have a problem describing the process as coding. This doesn't pertain to the question, but I end up taking the points to txt file and then use matlab, scatter3 to plot the points. Creating the "shape" points is my issue.
Both a circle and a cube edges set are 1-dimensional sets, so you can represent them as real intervals. For a circle it's straightforward: use an interval (0, 2pi) and transform a random value phi from the interval into a point:
xcentre + R cos(phi), ycentre + R sin(phi)
For a cube you have 12 segments, so use interval (0, 12) and split a random number from the interval into an integer part and a fraction. Then use the integer as an edge number and the fraction as a position inside the edge.
Easy variant:
First think of the min/max x/y values (separately; to reduce the faulty values for the step below), generate some coordinates matching this range, and then check if it fulfills eg. a^2+b^2=r^2 (circle)
If not, try again.
Better, but only possible for certain shapes:
Generate a radius between (0-max) and an angle (0-360)
(or just an angle if it should be on the circle border)
and use some math (sin/cos...) to transform it into x and y.
http://en.wikipedia.org/wiki/Polar_coordinate_system
I have a set of points in 3d. I form a line by joining these points. I have to obtain another line which is a shifted version of this line, such that the resultant shift is always to the right of the original line. What would be an approach to solve this problem? How to get the up vectors in the right direction each time?
Assume these points to lie on a sphere. Looking at the top view of the sphere i would want something like this
/\/\/\/\
/\/\/\/\
The first line is the original set of points and the second one the shifted set
Ok i am adding the code
std::vector<osg::Vec3> vertArray; // The array containig the 3d points
std::vector<osg::Vec3> shiftVec; // The array to store the shifted vectors
osg::Vec3 line1, line2, result, upVec, p1, p2, cross1, cross2;
result = vertArray[1] - vertArray[0];
result.normalise();
result = result X upVec; // i am not sure how to get this upvec for general set of points
shiftVec.push_back(result);
for(unsigned int i = 1; i < vertArray.size() - 1; i++)
{
line 1 = vertArray[i-1] - vertArray[i];
line 2 = vertArray[i+1] - vertArray[i];
line1.normalise();
line2.normalise();
upVec = line1 X line2;
line 1 = line1 X upVec;
p1 = vertArray[i-1] + line1;
line 2 = line2 X upVec;
p2 = vertArray[i+1] + line2;
cross1 = upVec;
cross2 = (p2-p1)X line2
float factor = cross2.lenght()/cross1.length();
result = p1+line1*factor;
result = result - vertArray[i];
shiftVec.push_back(result);
}
result = vertArray[i] - vertArray[i-1];
result.normalise();
result = result X upVec; // i am not sure how to get this upvec for general set of points
shiftVec.push_back(result);
look here: ECEF <-> ENU coordinates this might help
I rather use NEH local North,East,Height(or altitude) coordinate system
it is similar to compass + altimeter
if you are not looking in rotation axis direction (ECEF Z-axis) ... on poles
then North vector is just (0,0,6356754.7)-viewer_position (all in ECEF)
East,West vectors can be obtained as North x (0,0,6356754.7)
don`t remember if it is east or west (depends on your coordinate system and cross multiplicants order)
just check it visually and if wrong reverse the order or negate result
Up vector (Height or Altitude) is easy from this just Up=North x East or Up=North x West
again if wrong direction reverse order or negate result ...
[Notes]
6356754.7 [m] is earths polar radius
if you viewing from poles (ECEF Z-axis)
then North vector and Up vector lies on the same axis (in opposite direction)
which means there is no east or west (singularity)
on south hemisphere is usually used South instead of North
in that case South = (0,0,-6356754.7)-viewer_position
If you three points are A, B and C. Then the three point define a plane. In general the points will not lie on a (straight) line. If they do then it becomes ambiguous what "right" means. If everything is on a sphere, then the three points will define a curve formed by the intersection of the sphere and the plane. You could form another line my finding the intersection of the sphere with a parallel plane.
I'm not quite sure what you want but I'm guessing you want the second line to lie in a parallel plane. You can find the normal to the plane by taking the cross product N=(A-B) X (C-B). It looks like you are doing something like this but you need the ^ operator. See https://www8.cs.umu.se/kurser/TDBD12/VT04/lab/osg/html/doc++/osg/Vec3.html#DOC.2.224.21
upVec = line1 ^ line2;
I have to find all tiles that intersected by line segment but Bresenham's line algorithm doesnt fit to my requirements. I need to find all cells. I dont need to know intersection points, only the fact of intersection. Thanks for help.
I thought to find direction vector of line and step by step find cells by division on tile size. But i dont know how to select correct step size. 1 px step is bad i think.
Here is article of Amanatides and Woo "A Fast Voxel Traversal Algorithm for Ray Tracing" for 2D and 3D cases. Practical implementation.
You might use one of the many line equations found at: http://www.cut-the-knot.org/Curriculum/Calculus/StraightLine.shtml or http://mathworld.wolfram.com/Line.html
Supposedly you have your line in your coordinate system going through two points you deduce the y=mx+n equation and just match against your tiles and see if they intersect while moving x in the unit of your coordinate system in any direction you prefer from the smallest x of your tiles till the biggest. If your coordinate system is the screen, 1 pixel should be enough.
This is the closes I can hint right know without knowing more about the exact nature of the problem you are facing.
It is easy to modify the Bresenham's algorithm such that it tracks what you need. Here's the relevant fragment of the algorithm:
plot(x,y);
error = error + deltaerr;
if (error >= 0.5)
{
y = y + ystep;
error = error - 1.0;
}
To keep track of all the cells we need another variable. Note tat I have not rigorously checked this.
plot(x,y);
olderror = error.
error = error + deltaerr;
if (error >= 0.5)
{
y = y + ystep;
error = error - 1.0;
extra = error+olderror;
if (extra > 0)
{
plot (x,y); /* not plot (x-1,y); */
}
else if (extra < 0)
{
plot (x+1,y-1); /* not plot (x+1,y); */
}
else
{
// the line goes right through the cell corner
// either do nothing, or do both plot (x-1,y) and plot (x+1,y)
// depending on your definition of intersection
}
}
I have an array that represents a grid
For the sake of this example we will start the array at 1 rather that 0 because I realized after doing the picture, and can't be bothered to edit it
In this example blue would have an index of 5, green an index of 23 and red 38
Each color represents an object and the array index represents where the object is. I have implemented very simple gravity, whereby if the grid underneath is empty x + (WIDTH * (y + 1)) then the grid below is occupied by this object, and the grid that the object was in becomes empty.
This all works well in its current form, but what I want to do is make it so that red is the gravity point, so that in this example, blue will move to array index 16 and then 27.
This is not too bad, but how would the object be able to work out dynamically where to move, as in the example of the green grid? How can I get it to move to the correct index?
Also, what would be the best way to iterate through the array to 'find' the location of red? I should also note that red won't always be at 38
Any questions please ask, also thank you for your help.
This sounds very similar to line rasterization. Just imagine the grid to be a grid of pixels. Now when you draw a line from the green point to the red point, the pixels/cells that the line will pass are the cells that the green point should travel along, which should indeed be the shortest path from the green point to the red point along the discrete grid cells. You then just stop once you encounter a non-empty grid cell.
Look for Bresenham's algorithm as THE school book algorithm for line rasterization.
And for searching the red point, just iterate over the array linearly until you have it and then keep track of its grid position, like William already suggested in his answer.
x = x position
y = y position
cols = number of columns across in your grid
(y * cols) + x = index in array absolute value for any x, y
you could generalize this in a function:
int get_index(int x, int y, int gridcols)
{
return (gridcols * y) + x;
}
It should be noted that this works for ZERO BASED INDICES.
This is assuming I am understanding what you're talking about at all...
As for the second question, for any colored element you have, you should keep a value in memory (possibly stored in a structure) that keeps track of its position so you don't have to search for it at all.
struct _THING {
int xpos;
int ypos;
};
Using the get_index() function, you could find the index of the grid cell below it by calling like this:
index_below = get_index(thing.x, thing.y + 1, gridcols);
thing.y++; // increment the thing's y now since it has moved down
simple...
IF YOU WANT TO DO IT IN REVERSE, as in finding the x,y position by the array index, you can use the modulus operator and division.
ypos = array_index / total_cols; // division without remainder
xpos = array_index % total_cols; // gives the remainder
You could generalize this in a function like this:
// x and y parameters are references, and return values using these references
void get_positions_from_index(int array_index, int total_columns, int& x, int& y)
{
y = array_index / total_columns;
x = array_index % total_columns;
}
Whenever you're referring to an array index, it must be zero-based. However, when you are referring to the number of columns, that value will be 1-based for the calculations. x and y positions will also be zero based.
Probably easiest would be to work entirely in a system of (x,y) coordinates to calculate gravity and switch to the array coordinates when you finally need to lookup and store objects.
In your example, consider (2, 4) (red) to be the center of gravity; (5, 1) (blue) needs to move in the direction (2-5, 4-1) == (-3, 3) by the distance _n_. You get decide how simple you want n to be -- it could be that you move your objects to an adjoining element, including diagonals, so move (blue) to (5-1, 1+1) == (4, 2). Or perhaps you could move objects by some scalar multiple of the unit vector that describes the direction you need to move. (Say, heavier objects move further because the attraction of gravity is stronger. Or, lighter objects move further because they have less inertia to overcome. Or objects move further the closer they are to the gravity well, because gravity is an inverse square law).
Once you've sorted out the virtual coordinates of your universe, then convert your numbers (4, 2) via some simple linear formulas: 4*columns + 2 -- or just use multidimensional arrays and truncate your floating-point results to get your array indexes.