I am trying to write a raycasting algorithm described in this article. I use a coordinate system where y increases upward, x increases to the left. The variable names in the following code snippet are taken from the article, I will describe them so that the code is easier to read:
rayDirX and rayDirY are coordinates of the vector pointing from the player's position on the map in the direction where the ray is being cast, alpha is the angle between this ray and the origin.
Px, Py are precise coordinates of the player's position on the map
Ax, Ay are the precise coordinates of the first horizontal intersection, later other horizontal intersections.
Bx, By are the precise coordinates of the first vertical intersection, later other vertical intersections.
horXa, horYa, vertXa, vertYb are the step increments, which are constant after finding the first intersection. I suspect that the problem with the algorithm is that these values are not calculated correctly.
mapX, mapY are the coordinates of the lower left corner of an intersection on a map. This is used to check if there is a wall on this position in the array map[][].
Horizontal and vertical intersections are checked alternately. Ax, Ay, or Bx, By should hold the precise position of the intersection.
// Calculate the initial intersections
// Horizontal intersections
// If the ray is facing up
if (rayDirY > 0)
{
horYa = 1;
Ay = mapY+1;
}
// If the ray is facing down
else
{
horYa = -1;
Ay = mapY;
}
horXa = Math.abs(horYa)/tanAlpha;
Ax = Px + (Ay-Py)/tanAlpha;
// Vertical intersections
// If the ray is facing right
if (rayDirX > 0)
{
vertXa = 1;
Bx = mapX+1;
}
// If the ray is facing left
else
{
vertXa = -1;
Bx = mapX;
}
vertYa = Math.abs(vertXa)*tanAlpha;
By = Py + (Px-Bx)*tanAlpha;
//Loop to find where the ray hits a wall
//Number of texture to display
int texNum;
boolean horizontal = Math.abs(Ax * Math.cos(alpha)) < Math.abs(Bx*Math.cos(alpha));
//This loop runs for each ray with the setup above
while(true) {
// Check horizontal intersection
if (horizontal)
{
mapX = (int)Math.floor(Math.abs(Ax));
mapY = (int)Math.floor(Math.abs(Ay));
if(mapX > mapWidth-1) mapX = mapWidth-1;
if(mapY > mapHeight-1) mapY = mapHeight-1;
if(mapX < 0) mapX = 0;
if(mapY < 0) mapY = 0;
texNum = map[mapX][mapY];
if(texNum > 0) break;
Ax += horXa;
Ay += horYa;
horizontal = false;
}
else {
mapX = (int)Math.floor(Math.abs(Bx));
mapY = (int)Math.floor(Math.abs(By));
if(mapX > mapWidth-1) mapX = mapWidth-1;
if(mapY > mapHeight-1) mapY = mapHeight-1;
if(mapX < 0) mapX = 0;
if(mapY < 0) mapY = 0;
texNum = map[mapX][mapY];
if (texNum > 0) break;
Bx += vertXa;
By += vertYa;
horizontal = true;
}
}
Using this algorithm, the image is not rendered properly, see the screenshot. I suspect that it is because of horYa, Ay, vertXa, Bx not being calculated correctly. UPDATE: After some debugging efforts, it seems that sometimes we choose to compute horizontal intersection instead of vertical and vice versa... Really strange!
Can you please spot an error in the algorithm? Thank you for any input!
Related
I made the following picture to help visualize the problem:
You can control the monkey, by rotating it. You can step to the neigbour fields, but here I have a problem calculating which is the field to step.
The monkey has an angle, which goes from 0 to 360 degress, 0 is when its head is up. I would like to calculate the neigbour field from this angle. (Basically this field is where the orangutan "looks").
I store the neigbours, and also the vertices for every polygon.
I tried with the following idea:
Start a half line from the orangutan, then calculate the intersection with each side of the polygon. If we have an intersection, then we know the two endpoints. Then search through the neigbour fields, if one of them has both points in its vertices, then this is the good one.
Here the code I wrote:
public Field getNeighbourByAngle(double angle) {
// Field center
Point2D c = center;
// New point initial coordinates
double x = c.getX(); double y = c.getY()+1;
// Rotate the point by the angle
Point2D rotatedPoint = new Point2D(
-(x*Math.cos(Math.toRadians(angle)) - y*Math.sin(Math.toRadians(angle))),
y*Math.cos(Math.toRadians(angle)) + x*Math.sin(Math.toRadians(angle)) );
// Get the vector from the center to the roteted point
Point2D v = rotatedPoint.subtract(c);
// Iterate over the vertex arraylist
for (int i = 0; i<verts.size(); i++) {
// Get the two endpoints
Point2D p1 = verts.get(i);
Point2D p2;
if (i == verts.size() - 1) {
p2 = verts.get(0);
} else { p2 = verts.get(i+1);}
// Calculate the intersection
// Using the formula:
// x = c.x + v.x * t
// x = p1.x * (1-t) + p2.x * t
// These two are equals, get t from the equation:
double t = ( p1.getX() - c.getX( )) / (v.getX() + p1.getX() - p2.getX() );
// t has to be between [0, 1] because p1 <-> p2 is just a line segment
if (0 <= t && t<= 1) {
// Iterate over the neigbours
for (int j = 0; j<neighbours.size(); j++) {
// If the neigbour has both p1 and p2 in its vertices list, then we found the correct neigbour.
if (neighbours.get(j).getVerts().contains(p1) &&
neighbours.get(j).getVerts().contains(p2)) {
return neighbours.get(j);
}
}
}
}
return null;
}
But I not get the correct results, I don't know where is the problem.
I have a rectangle which when I hold down the mouse button I want that rectangle to move to that point following a strait line 1 pixel at a time.
This is my code so far (I put comments in it so you can understand)
float distanceX = finalX - x; //the number of pixels needed to get to destination on the X axis
float distanceY = finalY - y; // same as above but Y axis
float moveX = distanceX > 0 ? 1 : -1; // I only want it to move 1 pixel per render
float moveY = distanceY > 0 ? 1 : -1; // same as above
Array<Stuff> collidedX = new Array<Stuff>(); //saves collisions seperately for x and y
Array<Stuff> collidedY = new Array<Stuff>(); //because I want the square to move where the mouse is pointing even if it means only aligning one axis
for (Stuff s : collidables) {
if (overlapsT(s, x + moveX, y)) {
collidedX.add(s);
}
}
if (collidedX.size < 1) {
if (distanceX != 0)
x += moveX;
}
for (Stuff s : collidables) {
if (overlapsT(s, x, y + moveY)) {
collidedY.add(s);
}
}
if (collidedY.size < 1) {
if (distanceY != 0)
y += moveY;
}
right now the problem is it goes perfectly diagonal until it lines up with one of the axis and then moves up down left or right to the destination.
I don't want to move fractions of pixels. The way my custom physics engine works is each pixel matters, fractional pixels are no good so I am trying to figure out how to smooth the path or rather how to decide when to add 1 to x and then y.
Currently I can't comment, so I have to answer. I think the Bresenham's line algorithm will help you out. It's for drawing rasterize lines.
Bresenham
I'm trying to retrieve the real coordinates of an image, after the user has drawn a rectangle on a canvas over the image. For this I've used a LayerDrawable, with the bitmap image as the bottom layer. Unfortunately my solution works only, when the scale factor is 1 (no scaling has been performed). If the user zoomed and panned around a little bit with the current solution I get close to the coordinates, but something is amiss and they aren't accurate. Because I can't post the whole code here I've uploaded it to pastebin (link). I also do a little bit of preprocessing and scale the image exactly so it fits the available size of my extended ImageView. For that i use a ViewTreeObserver and on the predraw method I find out exactly how much space i have available and scale the image to that size, so it fits the bigger dimension and the aspect ratio is kept. The code for it is here (link).
The most important parts for this are:
private void fixCoordinates(){
//get utmost left,right,top,bottom corners from both begin and end coordinate
float left = Math.min(beginCoordinate.x, endCoordinate.x);
float top = Math.min(beginCoordinate.y, endCoordinate.y);
float right = Math.max(beginCoordinate.x, endCoordinate.x);
float bottom = Math.max(beginCoordinate.y, endCoordinate.y);
//reassign them to proper begin and end
PointF b = new PointF(left,top);
PointF e = new PointF(right,bottom);
//m[5] and m[2] denote offsets (empty spaces) when they are positive
if(m[2] > 0){
b.x = b.x - m[2];
e.x = e.x - m[2];
}
if(m[5] > 0){
b.y = b.y - m[5];
e.y = e.y - m[5];
}
//safety
if(b.x < 0){
b.x = 0;
}
if(b.y < 0){
b.y = 0;
}
if(e.x > layers[0].getIntrinsicWidth()){
e.x = layers[0].getIntrinsicWidth();
}
if(e.y > layers[0].getIntrinsicHeight()){
e.y = layers[0].getIntrinsicHeight();
}
//we only have one scale factor, because in the preprocessing we rescale and fit the image
setBeginCoordinate(b);
setEndCoordinate(e);
}
after the MotionEvent I use this function to set begin coordinates to be utmost top and left and end coordinates to be bottom and right.
the actual mapping to the original image is done here:
private PointF mapBeginCoordinates(PointF beginCoordinate, PointF endCoordinate){
float left = Math.min(beginCoordinate.x, endCoordinate.x);
float top = Math.min(beginCoordinate.y, endCoordinate.y);
double wAr = UtilFunctions.getAspectRatio(getOriginalWidth(), layers[0].getIntrinsicWidth());
double hAr = UtilFunctions.getAspectRatio(getOriginalHeight(), layers[0].getIntrinsicHeight());
left = (float)((double)left/wAr);
top = (float)((double)top/hAr);
float[] imageMatrix = new float[9];
getImageMatrix().getValues(imageMatrix);
float scaleFactorX = imageMatrix[Matrix.MSCALE_X];
float scaleFactorY = imageMatrix[Matrix.MSCALE_Y];
float fixedTransX = 1;
float fixedTransY = 1;
//m[5] height m[2] width
if(m[2] < 0){
fixedTransX = (m[2]*scaleFactorX);
}
if(m[5] < 0){
fixedTransY = (m[5]*scaleFactorY);
}
left = left/scaleFactorX + Math.abs(fixedTransX);
top = top/scaleFactorY + Math.abs(fixedTransY);
return new PointF(left,top);
}
the code is the same for the end coordinates. The code in pastebin is a bit messy, because I've been trying many different things for 2 days now to get it to work, yet something eludes me. I'll appreciate any help.
I managed to solve my issue by using the Mike Ortiz TouchImageView
the code I used to map the coordinates to the original bitmap is this:
private PointF mapBeginCoordinates(PointF beginCoordinate, PointF endCoordinate) {
//TODO we only have one aspect ratio for the current picture, so we should remove redundancy
float left = Math.min(beginCoordinate.x, endCoordinate.x);
float top = Math.min(beginCoordinate.y, endCoordinate.y);
double wAr = UtilFunctions.getAspectRatio(originalWidth, layers[0].getIntrinsicWidth());
double hAr = UtilFunctions.getAspectRatio(originalHeight, layers[0].getIntrinsicHeight());
if(!isZoomed()) {
left = (float) (left / wAr);
top = (float) (top / hAr);
}
if(isZoomed()) {
PointF b = transformCoordTouchToBitmap(left,top,true);
left = (float) (b.x / wAr);
top = (float) (b.y / hAr);
}
return new PointF(left,top);
}
i still use the fixcoordinates code from above to prepare my left/top - right/bottom positions. Thanks for the answers
I want to detect from some 2D points whenever a swipe is made.
The points are coming continuously, they come from a sensor. And are in 2D space, x and y.
Are there any algorithms for this?
I've tried something like this
float totalDistance = 0f;
float totalTime = 0f;
for (int i = points.size - 1; i > 0; i--) {
SwipePoint point1 = points.get(i); // last point added
SwipePoint point2 = points.get(i - 1); // second last point
totalDistance += point1.distance(point2);
if (totalDistance > MIN_SWIPE_DISTANCE
&& totalTime > MIN_SWIPE_TIME) {
// we have a swipe
listener.onSwipe(SwipeType.DOWM, 1f);
points.clear();
return;
}
}
Here i check if the length of the lines is bigger than a constant, and the same for the time, but is not showing anything.
Are there any algorithms for this?
Thanks.
Your algorithm does not seem to identify swipe, and one of the problems is in this line:
totalDistance += point1.distance(point2);
what it does it count total length traveled by a finger and will trigger a "swipe" even if finger travels for long enough with any trajectory.
What you should do is record x and y coordinates of a first point and when you encounter a point that is far enough from a start you can trigger a swipe.
The second problem in your algorithm is that you should check if swipe time is less than maximum swipe time. Also, you doesn't seem to update totalTime anywhere in your code.
The code should look like this (checking only vertical swipe):
SwipePoint startPoint = points.get(points.size - 1);
for (int i = points.size - 2; i > 0; i--) {
SwipePoint point = points.get(i); // last point added
int yDiff = Math.abs(startPoint.getY() - point.getY());
long totalTime = startPoint.getTime() - point.getTime();
if yDiff > MIN_SWIPE_DISTANCE
&& totalTime < MIN_SWIPE_TIME) {
// we have a swipe
listener.onSwipe(SwipeType.DOWM, 1f);
points.clear();
return;
}
}
So, I'm making a game where you are to outrun enemies. Instead of having the enemies move at only up, down, left, right, and at 45 degree angles, I want the enemy to take the shortest linear path towards the player. Here's my code:
public void moveEnemy() {
if (player.pos.x > enemy.pos.x) {
enemy.vel.x = 3;
}
if (player.pos.x < enemy.pos.x) {
enemy.vel.x = -3;
}
if (player.pos.y > enemy.pos.y) {
enemy.vel.y = 3;
}
if (player.pos.y < enemy.pos.y) {
enemy.vel.y = -3;
}
if (player.pos.x == enemy.pos.x) {
enemy.vel.x = 0;
}
if (player.pos.y == enemy.pos.y) {
enemy.vel.y = 0;
}
}
So, what this does is sets the velocity in cardinal directions. What could I do to make this more accurate?
Assuming you have the position of the player and enemy, and you want the enemy to always have a velocity of 3, then pull out your trigonometry textbook and do the following:
float h = Math.sqrt(Math.pow(enemy.pos.y-player.pos.y,2) + Math.pow(enemy.pos.x-player.pos.x,2));
float a = player.pos.x - enemy.pos.x;
float o = player.pos.y - enemy.pos.y;
enemy.vel.x = 3f*(a/h);
enemy.vel.y = 3f*(o/h);
What is this code doing, you ask? It's forming a triangle between the enemy and the player. You want the enemy to travel at 3 units/sec in the direction of the hypotenuse, so what you need to do is break that down into components that are parallel to the X and Y axes.
http://www.mathwords.com/s/sohcahtoa.htm
The floats h, a and o represent the hypotenuse, adjacent, and opposite sides of the triangle.
a/h is the velocity component parallel to the X axis.
o/h is the velocity component parallel to the y axis.
double spd=3;
double vX=player.pos.x-enemy.pos.x;
double vY=player.pos.y-enemy.pos.y;
double distance=Math.sqrt(vX*vX+vY*vY);
enemy.vel.x=vX/distance*spd;
enemy.vel.y=vY/distance*spd;
Calculates a vector pointing towards the position of palyer with a length of spd