Find the coordinate given the angle and distance from a point - java

I am trying to draw lines on android canvas by calculating the last point (X,Y) given a point (Xo,Yo), the distance and the angle. A diagram is illustrated below:
I am calculating the azimuth orientation angle degree using the below formula from magnetic sensor and accelerometer values
if (accelValues != null && magnetValues != null) {
float rotation[] = new float[9];
float orientation[] = new float[3];
if (SensorManager.getRotationMatrix(rotation, null, accelValues, magnetValues)) {
SensorManager.getOrientation(rotation, orientation);
float azimuthDegree = (float) (Math.toDegrees(orientation[0]) + 360) % 360;
orientationDegree = Math.round(azimuthDegree);
}
}
I am saving all lines in an array and afterwhich I am calling onDraw to repaint the canvas. Below are my codes for the onDraw and calculating the steps so it redraw the lines at each step the user is taking depending on his orientation. (Assuming the distance length is 60)
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
drawRotateImage(canvas);
canvas.drawPoint(Position.x_axis, Position.y_axis, paint);
for (Line l : listLine) {
canvas.drawLine(l.StartX, l.StartY, l.EndX, l.EndY, paint);
}
private void stepDetector () {
l = new Line();
l.setStartX(lastX);
l.setStartY(lastY);
l.setOrientationDegree(orientationDegree);
lineX = (float) (lastX + (60 * cos(orientationDegree)));
lineY = (float) (lastY + (60 * sin(orientationDegree)));
l.setEndX(lineX);
l.setEndY(lineY);
listLine.add(l);
System.out.println ("" + l.toString());
invalidate();
lastX = lineX;
lastY = lineY;
}
Problem I am facing is that the lines are not being drawn with the correct orientation. It is going in any direction irrespective of the orientation direction. I get something as shown below:
To what I have analyzed, the line are not being drawn accurately, given the orientationdegree. I believe it is something, relating to the Android Coordinate System. It would be grateful if someone can help me on this to calculate the exact orientation degree in any direction [0-360].
Is there a different equation for calculating the last point (X,Y) in each quadrant of the circle axis ?

I believe that the behaviour you are observing is due to the fact that your azimuth is in degrees whereas the trig functions expect radians. You could convert from degrees back to radians using Math.toRadians. However, I would stick to using radians everywhere and do away with calling Math.toDegrees (and stop rounding too).

Related

Processing angle calculation on rotated object

I am making a little ant colony simulation in Processing (4).
I have an Ant class, with a sense() , a move()and a render() function.
I also have a Food class with only a position PVector.
My sense class loops through all Foods in a given radius, and it is meant to only 'see' the ones inside a given view angle.
In my render() function I have an arc to visualise this (I do some division and addition so the arc centres in front of the rectangle):
void render() {
// Draw a rectangl rotated in the direction of velocity
float theta = velocity.heading() + radians(90);
if(detectFood) // a Boolean set in sense()
fill(0,173,67); // turns green
else {
stroke(255);
pushMatrix();
translate(position.x, position.y);
fill(200, 100);
rotate(theta); // I copied the rotation code from somewhere :)
rect(0-r/2,0-r,r,r*2); // r is a float used to control size
arc(0, 0, viewRadius * 2, viewRadius * 2, radians(270 - viewAngle/2), radians(270 + viewAngle/2)); // viewRadius is a float set in the constructor
popMatrix();
}
}
This ends up looking like this:
My sense() code uses trigonometry to calculate the angle and the distance (I am using my own calculations because wasn't sure the inbuilt ones did what I thought they did):
void sense() {
if (!detectFood) {
float closest = viewRadius;
Food selected = null;
for (Food fd : foods){
float foodDist = position.dist(fd.position);
if(foodDist <= viewRadius) {
float xs = position.x-fd.position.x;
float ys = position.y-fd.position.y;
float Angle = atan2(abs(ys), abs(xs));
float begin = radians(270 - viewAngle/2);
float end = radians(270 + viewAngle/2);
if(begin < Angle && Angle < end && foodDist < closest){
selected = fd;
closest = foodDist;
println("found food");
}
}
}
if (selected != null){
detectFood = true;
foodFocused = selected;
}
} else {
if(position.dist(foodFocused.position) < r) {
takeFood();
detectFood = false;
}
}
}
The problem is that because I rotate the shape (and the arc with it), my sensing code basically never works. Is there a way to account for rotation in trigonometry or maybe an easier way of doing this? Any help would be apreciated

Rotation animation with custom view

I have a custom View, IndicatorView, which is essentially a triangle that orients itself according to a specified angle of a circle with a radius equal to the triangle's length. The angle the triangle points to is frequently updated and I would like to animate between these two positions similar to how a hand on a clock moves. Below is an illustration of my custom view (not drawn proportionally or to scale; drawn according to the Android View coordinate plane):
In the IndicatorView class, I draw the triangle using a Path object and three PointF objects:
#Override
protected void onDraw(Canvas canvas){
path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
//a, b, and c are PointF objects
path.moveTo(a.x, a.y);
path.lineTo(b.x, b.y);
path.lineTo(c.x, c.y);
path.close();
canvas.drawPath(path, paint);
}
To calculate the different points, given the angle, I use parametric equations:
public void showAngle(){
//x = centerX + radius * cos(angle)
//y = centerY + radius * sin(angle)
//TODO sloppy; tidy up / optimize once finished
//centerX, centerY, length, and bottomWidth are all values
//calculated in onSizeChanged
a = new PointF((float) (centerX + (length * Math.cos(angle))), (float) (centerY + (length * Math.sin(angle))));
//perpendicular bilateral radius
double pRadius = bottomWidth / 2;
//perpendicular angle plus or minus 90 degrees depending on point
float pAngle = angle - 90;
pAngle = (pAngle < 0) ? 360 - Math.abs(pAngle) : pAngle;
pAngle = (pAngle > 360) ? pAngle % 360 : pAngle;
b = new PointF((float) (centerX + (pRadius * Math.cos(pAngle))), (float) (centerY + (pRadius * Math.sin(pAngle))));
pAngle = angle + 90;
pAngle = (pAngle < 0) ? 360 - Math.abs(pAngle) : pAngle;
pAngle = (pAngle > 360) ? pAngle % 360 : pAngle;
c = new PointF((float) (centerX + (pRadius * Math.cos(pAngle))), (float) (centerY + pRadius * Math.sin(pAngle)));
invalidate();
}
When I have a new angle, I use an ObjectAnimator to animate between the two angles. I place an AnimatorUpdateListener on the ObjectAnimator and call my showAngle() method in my IndicatorView using the intermediate values specified from the Animator:
public void updateAngle(float newAngle){
//don't animate to an angle if the previous angle is the same
if(view.getAngle() != newAngle){
if(anim != null && anim.isRunning()){
anim.cancel();
}
anim = ObjectAnimator.ofFloat(view, "angle", view.getAngle(), newAngle);
anim.setDuration(duration);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
if(view != null){
view.showAngle();
}
}
});
}
}
However, this code produces some strange and unexpected behavior:
The width size of the triangle changes somewhat drastically. This could be due to casting between different types but it shouldn't be that dramatic.
The point of the triangle never stops at the specified angle. Instead it just keeps moving in a circle.
The angle seems to dictate the animations speed rather than where the triangle should stop.
Sometimes it seems as though there are numerous triangles on the screen. This could be due to the speed, perhaps it's moving very fast.
Obviously, somewhere along the line my calculations must be incorrect, though, I'm struggling to find out where I went wrong. Question(s): Is there a more efficient way of getting my custom view to animate rotation to a given angle? If I am approaching this correctly, where am I going wrong?
So, the solution to my problem was rather simple but simple enough to be overlooked. The angle field that was being used for the calculations was in degrees and it just had to be converted to radians in order for it to work with the sin and cos methods.
Change all PointF instantiations, for instance:
a = new PointF((float) (centerX + (length * Math.cos(angle))), (float) (centerY + (length * Math.sin(angle))));
to use the angle in radians:
a = new PointF((float) (centerX + (length * Math.cos(Math.toRadians(angle))),
(float) (centerY + (length * Math.sin(Math.toRadians(angle)))));
Also, part of the problem was due to sound constantly being analyzed and the View being updated before the previous animation had time to render a few frames. This led to the IndicatorView hardly moving when the angle was being updated often and when it was not it would quickly move to its destination. This happens because the previous animation is canceled before another animation is set (which is necessary to prevent a delay). This is a tricky problem to fix but one optimization I found was to avoid starting a new animation if the current angle and the previous angle were relatively close to each other.
Hopefully this will be useful for someone stuck with a similar problem. This was all part of a guitar tuner project I was working on and the source can be found on GitHub.

Doubts about libgdx values for 3D animation

after creating a very simple animation using libGDX i have some questions I'd like to clarify in order to make sure I understand everything prior to start with any other development with more complexity.
I have a box created like this:
public void createBaseCube(Model modelCube) {
ModelBuilder modelBuilder = new ModelBuilder();
modelCube = modelBuilder.createBox(1f, 1f, 1f,
new Material(ColorAttribute.createDiffuse(Color.GREEN)),
VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal);
this.cubeInstance.transform.translate(0.5f, 0.5f, 0.5f);
}
As it is centered in position (0,0,0) and a want its corner to be allocated in (0, 0, 0) I applied the translation in last line.
Here is how it looks like (I added a set of tiles to have some reference):
Afterwards, I create the animation. I want to overturn the box, so it will be positioned over the white tile, and rotating over its bottom-right edge
public class CubeAnimation {
...
<<definition of attributes>>
...
public CubeAnimation (ModelInstance cubeModel, float fromAngle, float toAngle, float speed, float alpha){
this.cubeInstance = cubeModel;
this.fromAngle = fromAngle; //value set to 0
this.toAngle = toAngle; //value set to 90f
this.speed = speed; //value set to 1f
this.alpha = alpha; //value set to 0
}
public void update(float delta) {
alpha += delta * speed;
if (alpha >= 1f) {
finished =true;
return;
}
float angle = alpha * (toAngle - fromAngle);
fromAngle = angle;
Vector3 t = new Vector3(0.5f, -0.5f, 0);
cubeInstance.transform.idt().translate(t).rotate(Vector3.Z, -angle).translate(t.scl(-1));
}
Everything seems to be fine, and code is quite simple, BUT (and here are the issues) when applying the animation, the box is translated to the center again (so first translate when box was created is undone), and -surprise- although I'm passing 90f as parameter to the animation, cube only rotates 45 degrees (when I set 180, it rotated as expected: 90).
Here how it looks like after the animation:
What is wrong here? Thanks in advance for your help!
You want to rotate the cube from angle fromAngle to angle toAngle
You are attempting to do so gradually by calculating the percentage completed over time, stored in your alpha variable.
alpha += delta * speed;
if (alpha >= 1f) {
finished =true;
return;
}
This part is fine for calculating the percentage as an angular-velocity multiplied by time passed. ie
angle_percentage/seconds * seconds_passed
You then get the distance between the start and stop angles in this line
float angle = alpha * (toAngle - fromAngle);
This code works for a starting angle of 0, but will fail for non zero starting points. The equation for a line is y = mx + b, so to correct this, you should include the b value:
float angle = fromAngle + alpha * (toAngle - fromAngle);
This will start the animation at fromAngle and push it over the distance required.
The extra line fromAngle = angle; changes your starting location on every iteration, so you end up with an unpredictable animation, which will be different depending on the speed you choose... I'm fairly certain the factor of two is merely a coincidence ;)
Finally this loop ends when the value is set to 100%, but never actually updates to 100%. Try this instead.
public void update(float delta) {
alpha += delta * speed;
if (alpha <= 1f) {
float angle = fromAngle + alpha * (toAngle - fromAngle);
Vector3 t = new Vector3(0.5f, -0.5f, 0);
cubeInstance.transform.idt().translate(t).rotate(Vector3.Z, -angle).translate(t.scl(-1));
} else {
finished = true;
}
}

Translate Java 3D coordinates to 2D screen coordinates

I'm working with a Java 3D application called "Walrus" that is used to display directed graphs. The code already has a feature to highlight a node and draw label adjacent in graph given its screen coordinates.
Upon rotating the screen, the node is no more highlighted.
What I have is the node coordinates in 3D. I need to draw label to it.
Code for highlight using 3D coordinates
Point3d p = new Point3d();
m_graph.getNodeCoordinates(node, p);
PointArray array = new PointArray(1, PointArray.COORDINATES);
array.setCoordinate(0, p);
m_parameters.putModelTransform(gc);
gc.setAppearance(m_parameters.getPickAppearance());
How can I draw Label with 3D coordinates( Raster graphics throws error Renderer: Error creating immediate mode Canvas3D graphics context )
How can I convert 3D coordinates to 2D screen and use existing code to draw label at 2D screen point
Thanks,
Dakshina
I have an algorithm/method for converting [x,y,z] into [x,y] with the depth parameter:
The x value is : (int) (x - (z / depth * x))
The y value is : (int) (y - (z / depth * y))
Essentially, the depth is the focal point. The vanishing point will be at [0,0,depth].
Here's what i used to convert my 3D coordinates into perspective 2D, x2 and y2 being the 2dimensional coordinates, xyz being the 3D coordinates.
use these formulas:
x2 = cos(30)*x - cos(30)*y
y2 = sin(30)*x + sin(30)*y + z
I picked the angle 30 as it is easy for perspective purposes, also used in Isometric grids for drawing 3D on 2D papers. As the z axe will be the vertical one, x and y are the ones at 60 degrees from it right and left. Isometric Grid Picture.
I'm still working on rotation, but without altering the axes, just coordinate rotation in 3D.
Enjoy.
I found the solution.
This is the function to display Text3D at image 2D coordinates
public void drawLabel(GraphicsContext3D gc, double x, double y, int zOffset, String s) {
boolean frontBufferRenderingState = gc.getFrontBufferRendering();
gc.setBufferOverride(true);
gc.setFrontBufferRendering(true);
Point3d eye = getEye();
double labelZ = zOffset * LABEL_Z_OFFSET_SCALE
+ LABEL_Z_SCALE * eye.z + LABEL_Z_OFFSET;
double xOffset = LABEL_X_OFFSET * m_pixelToMeterScale;
double yOffset = LABEL_Y_OFFSET * m_pixelToMeterScale;
Point3d p = new Point3d(x + xOffset, y + yOffset, 0.0);
{
// Project given (x, y) coordinates to the plane z=labelZ.
// Convert from image-plate to eye coordinates.
p.x -= eye.x;
p.y -= eye.y;
double inversePerspectiveScale = 1.0 - labelZ / eye.z;
p.x *= inversePerspectiveScale;
p.y *= inversePerspectiveScale;
// Convert from eye to image-plate coordinates.
p.x += eye.x;
p.y += eye.y;
}
Transform3D scale = new Transform3D();
scale.set(LABEL_SCALE);
Vector3d t = new Vector3d(p.x, p.y, labelZ);
Transform3D translation = new Transform3D();
translation.set(t);
translation.mul(scale);
Transform3D transform = new Transform3D(m_imageToVworld);
transform.mul(translation);
gc.setModelTransform(transform);
//-----------------
int fontSize=(int)(10*m_magnification);
if(fontSize>20)
fontSize=20;
//---------------
// XXX: Courier may not be available on all systems.
Text2D text = new Text2D(s, new Color3f(1.0f, 1.0f, 1.0f),
"Courier", fontSize, Font.BOLD);
gc.draw(text);
gc.flush(true);
// NOTE: Resetting the model transform here is very important.
// For some reason, not doing this causes the immediate
// following frame to render incorrectly (but subsequent
// frames will render correctly). In some ways, this
// makes sense, because most rendering code assumes that
// GraphicsContext3D has been set to some reasonable
// transform.
gc.setModelTransform(m_objectTransform);
gc.setFrontBufferRendering(frontBufferRenderingState);
}
This is the function to take 3D coordinates and convert them to image 2D coordinates and render using above function
private boolean displayOnScreenLabel(int node, String label) {
boolean success = false;
try {
Transform3D transform = m_parameters.getObjectToEyeTransform();
Point3d nodeC = new Point3d();
m_graph.getNodeCoordinates(node, nodeC);
transform.transform(nodeC);
Point3d eye = m_parameters.getEye();
double perspectiveScale = 1.0 / (1.0 - nodeC.z / eye.z);
double centerX = eye.x + nodeC.x * perspectiveScale;
double centerY = eye.y + nodeC.y * perspectiveScale;
GraphicsContext3D gc = m_canvas.getGraphicsContext3D();
m_parameters.drawLabel(gc, centerX, centerY, m_labelZOffsetCounter++, label);
success = true;
} catch (final java.lang.OutOfMemoryError error) {
JOptionPane.showMessageDialog(m_frame, "The 3D Graphics is unable to find enough memory on your system. Kill the application!", "Out Of Memory!", JOptionPane.ERROR_MESSAGE);
} catch (Exception e) {
success = false;
}
return success;
}

quaternion to angle

Alright, so this is how I am doing it:
float xrot = 0;
float yrot = 0;
float zrot = 0;
Quaternion q = new Quaternion().fromRotationMatrix(player.model.getRotation());
if (q.getW() > 1) {
q.normalizeLocal();
}
float angle = (float) (2 * Math.acos(q.getW()));
double s = Math.sqrt(1-q.getW()*q.getW());
// test to avoid divide by zero, s is always positive due to sqrt
// if s close to zero then direction of axis not important
if (s < 0.001) {
// if it is important that axis is normalised then replace with x=1; y=z=0;
xrot = q.getXf();
yrot = q.getYf();
zrot = q.getZf();
// z = q.getZ();
} else {
xrot = (float) (q.getXf() / s); // normalise axis
yrot = (float) (q.getYf() / s);
zrot = (float) (q.getZf() / s);
}
But it doesn't seem to work when I try to put it into use:
player.model.addTranslation(xrot * player.speed, 0, zrot * player.speed);
AddTranslation takes 3 numbers to move my model by than many spaces (x, y, z), but hen I give it the numbers above it doesn't move the model in the direction it has been rotated (on the XZ plane)
Why isn't this working?
Edit: new code, though it's about 45 degrees off now.
Vector3 move = new Vector3();
move = player.model.getRotation().applyPost(new Vector3(1,0,0), move);
move.multiplyLocal(-player.speed);
player.model.addTranslation(move);
xrot, yrot, and zrot define the axis of the rotation specified by the quaternion. I don't think you want to be using them in your addTranslation() call...in general, that won't have anything to do with the direction of motion.
What I mean by that is: your 3-D object -- let's say for the sake of argument that it's an airplane -- will have a certain preferred direction of motion in its original coordinate
system. So if the original orientation has the center of mass at the origin, and the
propeller somewhere along the +X axis, the plane wants to fly in the +X direction.
Now you introduce some coordinate transformation that rotates the airplane into some other orientation. That rotation is described by a rotation matrix, or equivalently, by a
quaternion. Which way does the plane want to move after the rotation?
You could find
that by taking a unit vector in the +X direction: (1.0, 0.0, 0.0), then applying the
rotation matrix or quaternion to that vector to transform it into the new coordinate
system. (Then scale it by the speed, as you're doing above.) The X, Y, and Z components
of the transformed, scaled vector give the desired incremental motion along each axis. That transformed vector is generally not going to be the rotation axis of the quaternion, and I think that's probably your problem.

Categories