Calculate target Pixels from given Angle - java

What i want :
I am using Orientation Sensor to get Azmuth value (Angle). i am also taking starting point from user and draw a circle. Now i want to draw next pixel on the point where the user is heading considering that one step is equal to 30 pixels.
As user starts walking i want to draw circles of user current position on the image of floor plan inserted on screen. I can't use GPS for this solution due to certain reasons.
Here are steps i am performing :
Get current user direction in angles from Orientation sensor.
User will touch on screen to draw starting point on image.
As user starts walking it will draw points on the image relative to user's real world direction like i showed in above image. How can i certainly achieve this given only starting point(pixelX,pixelY) and start angle of user and current angle where he just faced.
What we have achieved so far :
We can draw straight lines on 4 angles i.e 0 , 90,180 and 270 by just adding and subtracting pixels to current pixels.
newAzimuth is current angle of user direction
if (newAzimuth >= 45 && newAzimuth <= 135) {
startX = startX + oneStepPixelsWidth;
mScreenRotationTextView.setText("You turned (Right)");
} else if (newAzimuth > 135 && newAzimuth <= 225) {
mScreenRotationTextVniew.setText("You turned (Back)");
startY = startY + oneStepPixelsHeight;
} else if (newAzimuth > 225 && newAzimuth <= 315) {
mScreenRotationTextView.setText("You turned (Left)");
startX = startX - oneStepPixelsWidth;
} else if (newAzimuth > 315 || newAzimuth < 45) {
mScreenRotationTextView.setText("You turned (Front)");
startY = startY - oneStepPixelsHeight;
}

Given that calculated angles are:
Here's the equations for that.
X=distance*cos(angle)
Y=distance*sin(angle)
In your case distance will always be 30 pixel
so (30Cos(Angle),30Sin(Angle)) will give you your location.
To adjust your calculated angle to work you can rotate them with those formulas;
adjustedX = x cos(angle) − y sin(angle)
adjustedY = y cos(angle) + x sin(angle)
By exemple if the angle calculated are like this:
Then you will need to;
Rotate 90 degree right or 270 degree left.
Translate.
Rotate 270 degree right or 90 degree left.
private Pair<Double, Double> getPositionOf(Pair<Double, Double> lastPosition, double angle, int distance, int angleAdjustment)
{
final Pair<Double, Double> rotatedLeftPosition = rotateLeft(lastPosition, 360 - angleAdjustment);
final Pair<Double, Double> translatedLocation = applyTranslationTo(rotatedLeftPosition, angle, distance);
return rotateLeft(translatedLocation, angleAdjustment);
}
private Pair<Double, Double> rotateLeft(Pair<Double, Double> position, double degreeAngle)
{
double x = position.first;
double y = position.second;
double adjustedX = (x * Math.cos(degreeAngle)) - (y * Math.sin(degreeAngle));
double adjustedY = (y * Math.cos(degreeAngle)) + (x * Math.sin(degreeAngle));
return new Pair<>(adjustedX, adjustedY);
}
#NotNull
private Pair<Double, Double> applyTranslationTo(final Pair<Double, Double> position, final double angle, final int distance)
{
double x = distance * Math.cos(angle);
double y = distance * Math.sin(angle);
return new Pair<>(position.first + x, position.second + y);
}
Where angleAdjustment will be 90

Related

java robot turning circle understeering

I am building a robot in java and I am implementing a turning system which calculates the angle between two coordinates (x1, y1), (x2, y2), however the robot is currently underturning i.e. if i told it to turn 90 degress it would only turn 40 so I need to input more steering. The robot can turn left or right dependning on whether the turn required is greater than 180. Below the code works but only if the starting heading is at 0 degrees so i need the code to take into consideration the current position.
double xDiff = x2 - x1;
double yDiff = y2 - y1;
double angle = Math.toDegrees(Math.atan2(yDiff, xDiff));
double currentAngle = 0; //is changed after first run
angle = (angle + 360) % 360;
angle = angle - currentAngle;
makes the angle between -180 and 180
if (angle > 180)
angle -= 360;
if(angle < 0 )
{
angle = angle - 45;
}
else if(angle > 0)
{
angle = angle + 45;
}
Don't use angles for this. (Don't use angles for anything, if you can possibly avoid it.) If the dot product between this frame's [xDiff,yDiff] and the current heading vector is negative, the angle is greater than 180 degrees. (Also, the perp dot product indicates whether you need to turn left or right.)

How to move a sprite along a line a certain amount of pixels at a time?

If you have a sprite at position (100, 100) and want to move it along a line to position (200, 300), how do you do so in increments of say 5 pixels max. At the moment, I am simply using the difference in x/y position to calculate the length and move the sprite 1/10 of the length at a time.
Instead I want it to move by 5 pixels max. So for this example, as the next y position is 2x as far away than the next x position, it should be moving 2x further in the y direction such that the sprite arrives at the position via a straight line.
Current code:
public void move() {
double currentX = this.getCenterX();
double currentY = this.getCenterY();
double nextX = next.getCenterX();
double nextY = next.getCenterY();
// Used to jump by 1/10 edge length
double xDif = Math.abs(currentX - nextX);
double yDif = Math.abs(currentY - nextY);
// Move by 1/10 the length in correct direction
if (currentX > nextX) {
this.setX(this.getX() - (xDif/10));
} else if (currentX < nextX) {
this.setX(this.getX() + (xDif/10));
}
if (currentY > nextY) {
this.setY(this.getY() - (yDif/10));
} else if (currentY < nextY) {
this.setY(this.getY() + (yDif/10));
}
}
To get the movement vector you first need to get the direction vector in order to get the direction's unit vector (a vector which is one in length).
The direction vector is the delta (difference) between your start and finish points x1 - x0, y1 -y0.
To get the unit vector, you take each vector component (x, y) and divide it by the vectors total magnitude sqrt(x^2 + y^2).
double xDirection = currentX - nextX;
double yDirection = currentY - nextY;
double magnitude = Math.sqrt(xDirection*xDirection + yDirection*yDirection);
double xUnit = xDirection/magnitude;
double yUnit = yDirection/magnitude;
Now if you want to move only 5 pixels total, then you can make your movement vector by multiplying each component of the unit vector by 5:
double xMovement = xUnit * 5;
double yMovement = yUnit * 5;
this.setX(this.getX() + xMovement);
this.setY(this.getY() + yMovement);

Get the coordinates at the edge of the screen from a given angle

I know the startpoint (the middle of the screen) and the angle (in my example 20°). Now I want to know the position on the edge of the screen, like an invisible line is drawn from the center to the edge in the given angle. For better explanation, I included an image:
One way to do this is to calculate a point on a circle with a radius equal to or greater than the maximum diagonal, and then just clip it to the screen boundary.
Using Pythagoras' theorem, the length of the maximum diagonal will be
float d = Math.sqrt((width/2)*(width/2) + (height/2)*(height/2));
So you can calculate the point on the circle like this (angle is in radians clockwise from the top):
float x = Math.sin(angle) * d;
float y = -Math.cos(angle) * d;
Then you have to clip the vector from the origin to the point to each of the 4 sides, e.g for the right and left sides:
if(x > width/2)
{
float clipFraction = (width/2) / x; // amount to shorten the vector
x *= clipFraction;
y *= clipFraction;
}
else if(x < -width/2)
{
float clipFraction = (-width/2) / x; // amount to shorten the vector
x *= clipFraction;
y *= clipFraction;
}
Also do this for height/2 and -height/2. Then finally you can add width/2, height/2 to x and y to get the final position (with the center of the screen being width/2, height/2 not 0,0):
x += width/2
y += height/2

How to know if Android device is Flat on table

I'm using accelometer sensor to detect whether my device is flat on a table or not. The weird thing is even when I put my phone flat or rotate it on it's side the value is always between 90 and 100! This shouldn't be correct! am I missing something? Here is my code:
float[] values = event.values;
// Movement
float x = values[0];
float y = values[1];
float z = values[2];
float norm_Of_g =(float) Math.sqrt(x * x + y * y + z * z);
// Normalize the accelerometer vector
x = (x / norm_Of_g);
y = (y / norm_Of_g);
z = (z / norm_Of_g);
int inclination = (int) Math.round(Math.toDegrees(Math.acos(y)));
Log.i("tag","incline is:"+inclination);
if (inclination < 25 || inclination > 155)
{
// device is flat
Toast.makeText(this,"device flat - beep!",Toast.LENGTH_SHORT);
}
Edit: I'm using this code : How to measure the tilt of the phone in XY plane using accelerometer in Android
You're using the y-axis instead of the z-axis as used in the answer you linked.
The value of acos will be near-zero when the argument is near one (or near 180 degrees when near negative one), as seen in this picture:
As such, your inclination will be near zero (or 180) degrees only when the y axis is normalized to about one or negative one, eg when it is parallel to gravity, (thus, the device is "standing up").
If there's no other error, simply switching from:
int inclination = (int) Math.round(Math.toDegrees(Math.acos(y)));
to
int inclination = (int) Math.round(Math.toDegrees(Math.acos(z)));
should do it.
I used the code on this page Motion Sensors
public void onSensorChanged(SensorEvent event){
// In this example, alpha is calculated as t / (t + dT),
// where t is the low-pass filter's time-constant and
// dT is the event delivery rate.
final float alpha = 0.8;
// Isolate the force of gravity with the low-pass filter.
gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];
// Remove the gravity contribution with the high-pass filter.
linear_acceleration[0] = event.values[0] - gravity[0];
linear_acceleration[1] = event.values[1] - gravity[1];
linear_acceleration[2] = event.values[2] - gravity[2];
}
The intention of this is to factor out the force of gravity (over repeated measurements) in the accelerometer values, leaving just the acceleration component in each direction.
The force of gravity is measured along each axis, so once you know which axis represents the device lying flat, you can just check if the majority of the force of gravity lays in just that axis. That would mean the device is laying flat on the table.
You can also look at the linear acceleration to make sure the device isn't moving.

How to fix circle and rectangle overlap in collision response?

Since in the digital world a real collision almost never happens, we will always have a situation where the "colliding" circle overlaps the rectangle.
How to put back the circle in the situation where it collides perfectly with the rectangle without overlap?
Suppose that the rectangle is stopped (null velocity) and axis-aligned.
I would solve this problem with a posteriori approach (in two dimensions).
In short I have to solve this equation for t:
Where:
is a number that answers to the question: how many frames ago did the
collision happen perfectly?
is the radius of the circle.
is the center of the circle
is its velocity.
and are functions that return the x and y coordinates of
the point where the circle and the rectangle collide (when the circle is
at position, that is in the position in which perfectly collide with the rectangle).
Recently I solved a similar problem for collisions between circles, but now I don't know the law of the functions A and B.
After years of staring at this problem, and never coming up with a perfect solution, I've finally done it!
It's pretty much a straight forward algorithm, no need for looping and approximations.
This is how it works at a higher level:
Calculate intersection times with each side's plane IF the path from current point to future point crosses that plane.
Check each side's quadrant for single-side intersection, return the intersection.
Determine the corner that the circle is colliding with.
Solve the triangle between the current point, the corner, and the intersecting center (radius away from the corner).
Calculate time, normal, and intersection center.
And now to the gory details!
The input to the function is bounds (which has a left, top, right, bottom) and a current point (start) and a future point (end).
The output is a class called Intersection which has x, y, time, nx, and ny.
{x, y} is the center of the circle at intersection time.
time is a value from 0 to 1 where 0 is at start and 1 is at end
{nx, ny} is the normal, used for reflecting the velocity to determine the new velocity of the circle
We start off with caching variables we use often:
float L = bounds.left;
float T = bounds.top;
float R = bounds.right;
float B = bounds.bottom;
float dx = end.x - start.x;
float dy = end.y - start.y;
And calculating intersection times with each side's plane (if the vector between start and end pass over that plane):
float ltime = Float.MAX_VALUE;
float rtime = Float.MAX_VALUE;
float ttime = Float.MAX_VALUE;
float btime = Float.MAX_VALUE;
if (start.x - radius < L && end.x + radius > L) {
ltime = ((L - radius) - start.x) / dx;
}
if (start.x + radius > R && end.x - radius < R) {
rtime = (start.x - (R + radius)) / -dx;
}
if (start.y - radius < T && end.y + radius > T) {
ttime = ((T - radius) - start.y) / dy;
}
if (start.y + radius > B && end.y - radius < B) {
btime = (start.y - (B + radius)) / -dy;
}
Now we try to see if it's strictly a side intersection (and not corner). If the point of collision lies on the side then return the intersection:
if (ltime >= 0.0f && ltime <= 1.0f) {
float ly = dy * ltime + start.y;
if (ly >= T && ly <= B) {
return new Intersection( dx * ltime + start.x, ly, ltime, -1, 0 );
}
}
else if (rtime >= 0.0f && rtime <= 1.0f) {
float ry = dy * rtime + start.y;
if (ry >= T && ry <= B) {
return new Intersection( dx * rtime + start.x, ry, rtime, 1, 0 );
}
}
if (ttime >= 0.0f && ttime <= 1.0f) {
float tx = dx * ttime + start.x;
if (tx >= L && tx <= R) {
return new Intersection( tx, dy * ttime + start.y, ttime, 0, -1 );
}
}
else if (btime >= 0.0f && btime <= 1.0f) {
float bx = dx * btime + start.x;
if (bx >= L && bx <= R) {
return new Intersection( bx, dy * btime + start.y, btime, 0, 1 );
}
}
We've gotten this far so we know either there's no intersection, or it's collided with a corner. We need to determine the corner:
float cornerX = Float.MAX_VALUE;
float cornerY = Float.MAX_VALUE;
if (ltime != Float.MAX_VALUE) {
cornerX = L;
} else if (rtime != Float.MAX_VALUE) {
cornerX = R;
}
if (ttime != Float.MAX_VALUE) {
cornerY = T;
} else if (btime != Float.MAX_VALUE) {
cornerY = B;
}
// Account for the times where we don't pass over a side but we do hit it's corner
if (cornerX != Float.MAX_VALUE && cornerY == Float.MAX_VALUE) {
cornerY = (dy > 0.0f ? B : T);
}
if (cornerY != Float.MAX_VALUE && cornerX == Float.MAX_VALUE) {
cornerX = (dx > 0.0f ? R : L);
}
Now we have enough information to solve for the triangle. This uses the distance formula, finding the angle between two vectors, and the law of sines (twice):
double inverseRadius = 1.0 / radius;
double lineLength = Math.sqrt( dx * dx + dy * dy );
double cornerdx = cornerX - start.x;
double cornerdy = cornerY - start.y;
double cornerdist = Math.sqrt( cornerdx * cornerdx + cornerdy * cornerdy );
double innerAngle = Math.acos( (cornerdx * dx + cornerdy * dy) / (lineLength * cornerdist) );
double innerAngleSin = Math.sin( innerAngle );
double angle1Sin = innerAngleSin * cornerdist * inverseRadius;
// The angle is too large, there cannot be an intersection
if (Math.abs( angle1Sin ) > 1.0f) {
return null;
}
double angle1 = Math.PI - Math.asin( angle1Sin );
double angle2 = Math.PI - innerAngle - angle1;
double intersectionDistance = radius * Math.sin( angle2 ) / innerAngleSin;
Now that we solved for all sides and angles, we can determine time and everything else:
// Solve for time
float time = (float)(intersectionDistance / lineLength);
// If time is outside the boundaries, return null. This algorithm can
// return a negative time which indicates the previous intersection.
if (time > 1.0f || time < 0.0f) {
return null;
}
// Solve the intersection and normal
float ix = time * dx + start.x;
float iy = time * dy + start.y;
float nx = (float)((ix - cornerX) * inverseRadius);
float ny = (float)((iy - cornerY) * inverseRadius);
return new Intersection( ix, iy, time, nx, ny );
Woo! That was fun... this has plenty of room for improvements as far as efficiency goes. You could reorder the side intersection checking to escape as early as possible while making as few calculations as possible.
I was hoping there would be a way to do it without trigonometric functions, but I had to give in!
Here's an example of me calling it and using it to calculate the new position of the circle using the normal to reflect and the intersection time to calculate the magnitude of reflection:
Intersection inter = handleIntersection( bounds, start, end, radius );
if (inter != null)
{
// Project Future Position
float remainingTime = 1.0f - inter.time;
float dx = end.x - start.x;
float dy = end.y - start.y;
float dot = dx * inter.nx + dy * inter.ny;
float ndx = dx - 2 * dot * inter.nx;
float ndy = dy - 2 * dot * inter.ny;
float newx = inter.x + ndx * remainingTime;
float newy = inter.y + ndy * remainingTime;
// new circle position = {newx, newy}
}
And I've posted the full code on pastebin with a completely interactive example where you can plot the starting and ending points and it shows you the time and resulting bounce off of the rectangle.
If you want to get it running right away you'll have to download code from my blog, otherwise stick it in your own Java2D application.
EDIT:
I've modified the code in pastebin to also include the collision point, and also made some speed improvements.
EDIT:
You can modify this for a rotating rectangle by using that rectangle's angle to un-rotate the rectangle with the circle start and end points. You'll perform the intersection check and then rotate the resulting points and normals.
EDIT:
I modified the code on pastebin to exit early if the bounding volume of the path of the circle does not intersect with the rectangle.
Finding the moment of contact isn't too hard:
You need the position of the circle and rectangle at the timestep before the collision (B) and the timestep after (A). Calculate the distance from the center of the circle to the line of the rectangle it collides with at times A and B (ie, a common formula for a distance from a point to a line), and then the time of collision is:
tC = dt*(dB-R)/(dA+dB),
where tC is the time of collision, dt is the timestep, dB is the distance to line before the collision, dA is the distance after the collision, and R is the radius of the circle.
This assumes everything is locally linear, that is, that your timesteps are reasonably small, and so that the velocity, etc, don't change much in the timestep where you calculate the collision. This is, after all, the point of timesteps: in that with a small enough timestep, non-linear problems are locally linear. In the equation above I take advantage of that: dB-R is the distance from the circle to the line, and dA+dB is the total distance moved, so this question just equates the distance ratio to the time ratio assuming everything is approximately linear within the timestep. (Of course, at the moment of collision the linear approximation isn't its best, but to find the moment of collision, the question is whether it's linear within a timestep up to to moment of collision.)
It's a non-linear problem, right?
You take a time step and move the ball by its displacement calculated using velocity at the start of the step. If you find overlap, reduce the step size and recalculate til convergence.
Are you assuming that the balls and rectangles are both rigid, no deformation? Frictionless contact? How will you handle the motion of the ball after contact is made? Are you transforming to a coordinate system of the contact (normal + tangential), calculating, then transforming back?
It's not a trivial problem.
Maybe you should look into a physics engine, like Box2D, rather than coding it yourself.

Categories