Why won't my Quaternion rotate properly? - java

Ok, we are not talking about OpenGL with this question, but this will be used with OpenGL ES 2.0.
Question: How do create and rotate a Quaternion with the following code?
I have been reading up and studying about this and still can't quite gasp the concepts. I thought I understood it, but once I started making some calculations to rotate the quaternion I realized I can't even get back to where I started.
So let us say that we have a cube, and the center of it is at (0, 0, 0). We want to rotate it on the x-axis by 45 degrees. What would I do? (Only the Quaternion)
Assuming success, how would you get the amount of rotation from 'W'? I know that '1' indicates that there is no rotation, but what if it was rotated 173 degrees?
Trying to rotate to a given direction, 45 degrees, and then get that value from W. I feel like I need to convert the angle to rads or something, but not exactly sure. Tutorials online vary from one to the next.
Here is my code:
import java.util.Scanner;
import Quaternion;
public class Main {
public static void main(String[] args) {
Quaternion q1 = new Quaternion(0, 0, 0, 1);
Quaternion q2 = new Quaternion(0, 0, 0, (float) Math.cos(toRAD(45.0f) / 2));
q1 = q2.mul(q1);
System.out.println("q1: " + q1.getX() + ", " + q1.getY() + ", " + q1.getZ() + " with " + toANGLE(2.0f * Math.acos(q1.getW())));
}
private static double toRAD(float angle) {
return angle * (Math.PI / 180.0f);
}
private static float toANGLE(double rad) {
return (float) (rad * (180.0f / Math.PI));
}
}
Here is the code for a Quaternion:
public class Quaternion // Credit goes to 'thebennybox' (http://www.youtube.com/user/thebennybox)
{
private float x;
private float y;
private float z;
private float w;
public Quaternion(float x, float y, float z, float w)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
public float length()
{
return (float)Math.sqrt(x * x + y * y + z * z + w * w);
}
public Quaternion normalize()
{
float length = length();
return new Quaternion(x / length, y / length, z / length, w / length);
}
public Quaternion conjugate()
{
return new Quaternion(-x, -y, -z, w);
}
public Quaternion mul(Quaternion r)
{
float w_ = w * r.getW() - x * r.getX() - y * r.getY() - z * r.getZ();
float x_ = x * r.getW() + w * r.getX() + y * r.getZ() - z * r.getY();
float y_ = y * r.getW() + w * r.getY() + z * r.getX() - x * r.getZ();
float z_ = z * r.getW() + w * r.getZ() + x * r.getY() - y * r.getX();
return new Quaternion(x_, y_, z_, w_);
}
public Quaternion mul(Vector3f r)
{
float w_ = -x * r.getX() - y * r.getY() - z * r.getZ();
float x_ = w * r.getX() + y * r.getZ() - z * r.getY();
float y_ = w * r.getY() + z * r.getX() - x * r.getZ();
float z_ = w * r.getZ() + x * r.getY() - y * r.getX();
return new Quaternion(x_, y_, z_, w_);
}
public float getX()
{
return x;
}
public void setX(float x)
{
this.x = x;
}
public float getY()
{
return y;
}
public void setY(float y)
{
this.y = y;
}
public float getZ()
{
return z;
}
public void setZ(float z)
{
this.z = z;
}
public float getW()
{
return w;
}
public void setW(float w)
{
this.w = w;
}
}

I'm still not 100% sure what your question is asking, but I'll give it a shot.
Problem: Given a quaternion representing a 0 degree rotation about x, y, z, generate a new quaternion representing a 45 degree rotation about the x axis
Start with a quaternion representing no rotation, call it q1
q1 = (w1, x1, y1, z1)
q1.w1 = cos(0/2) = 1
q1.x1 = 0 * sin(0/2) = 0
q1.y1 = 0 * sin(0/2) = 0
q1.z1 = 0 * sin(0/2) = 0
So q1 = (1, 0, 0, 0)
Generate a new rotation that is 45 degrees (PI/4 radians) about the X axis
We need a temporary quaternion to modify q1. Let's call it q2.
q2 = (w2, x2, y2, z2)
q2.w2 = cos(PI/4 / 2) = cos(PI/8)
q2.x2 = 1.0 * sin(PI/4 / 2) = 1.0 * sin(PI/8) = sin(PI/8)
q2.y2 = 0.0 * sin(PI/4 / 2) = 0.0
q2.z2 = 0.0 * sin(PI/4 / 2) = 0.0
so q2 = (cos(PI/8), sin(PI/8), 0, 0)
Now this last step is important, you modify your original quaternion by a left-hand multiplication of the temporary quaternion
What I mean is this:
q1 = q2 * q1
Your multiplication function is written correctly, so the problem is not there. Remember that quaternion multiplications are not commutative. That is q2 * q1 is NOT the same as q1*q2!
At this point q1 is modified to represent a 45 degree rotation about the X axis.
To print out the angle in degrees, you need to compute 2.0 * acos(q1.w) / PI * 180
Your code is incorrectly computing q1.w/PI * 180 to get the angle in degrees.
More specifically, change
toANGLE(resQuat.getW())
to
toANGLE(2.0f * Math.acos(resQuat.getW()))
I haven't looked at your code beyond that, but try applying these concepts and see if that fixes your problem.

Related

How to calculate height of a virtual ball being thrown using mobile accelerometer

I'm using the function
acc = (float) Math.sqrt(x * x + y * y + z * z) - GRAVITY_EARTH;
to calculate the initial acceleration when a ball is being thrown. X, y and z are the values from the Android accelerometer.
I also have a counter that counts up when the throwing motion exceeds the minimum required acceleration, and I'm trying to update the height of a virtual ball and output its position and speed, using this formula:
vel = acc + (-GRAVITY_EARTH * Float.parseFloat(timeCounter));
But this only continuously applies the accelerometer force, how can I get the initial throwing speed and then calculate the height of the ball in real-time?
Accelerometer function below:
/* Get values from the built in accelerometer while they are being updated. */
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
float x = event.values[0];
float y = event.values[0];
float z = event.values[0];
acc = (float) Math.sqrt(x * x + y * y + z * z) - GRAVITY_EARTH;
String a = String.format("%.3f", acc);
a = a.replace(",", ".");
currentAccText.setText(String.valueOf(a));
/* Is the current acc larger than set minimum value? */
if (acc >= minAcc) {
/* Has the timer not been started yet? */
if (timeCounter.equals("0.0")) {
timer.start();
a = String.format("%.3f", acc);
a = a.replace(",", ".");
thrownAccText.setText(a);
String hr = String.format("%.3f", heightRecord);
hr = hr.replace(",", ".");
recordHeightText.setText(hr);
}
}
vel = acc + (-GRAVITY_EARTH * Float.parseFloat(timeCounter));
height += vel;
heightBar.setProgress((int) height);
if (vel < 0.01f && vel > -0.01f) sound.start();
if (height > heightRecord) heightRecord = height;
if (height < 0.01f) height = 0.f;
String h = String.format("%.3f", height);
h = h.replace(",", ".");
heightText.setText(h);
if (height > 0.f) throwOrGroundText.setText("Throw!");
else throwOrGroundText.setText("On the ground.");
}
}

Rotating a plane on a point

I have a Java class that creates a virtual screen (let's call it a map) that can be translated, resized, and rotated.
When I rotate it, however, it only rotates it around (0, 0).
To transform the point to the screen you first rotate it, then resize it, then translate it.
private double dx; //offset in x and y
private double dy;
private double t; //rotation (radians)
private double sx; //scale of x and y
private double sy;
public double[] toScreen(double x, double y) //takes (x, y) on the map and gives (x1, y1) for the screen
{
double[] xy = {x, y};
if(t != 0)
{
double distance = Math.hypot(xy[0], xy[1]);
double theta = Math.atan2(xy[1], xy[0]) + t;
xy[0] = Math.cos(theta)*distance;
xy[1] = Math.sin(theta)*distance;
}
xy[0] *= sx;
xy[1] *= sy;
xy[0] += dx;
xy[1] += dy;
return xy;
}
to set the rotation or change it, you manipulate the variable t, but it rotates on (0, 0).
If I make a method that takes in (x, y) to rotate around like public void changeRotation(double t, double x, double y).
I want (x, y) to be map coordinates. What would the method look like and can you explain what it does?
If I understood correctly this is what you need :
/**
* #param point point (x,y) of the coordinates to be rotated
* #param center point (x,y) of the center (pivot) coordinates
* #param angle in radians
* #return point (x,y) of the new (translated) coordinates
*/
static Point2D.Double rotateAPoint(Point2D.Double point, Point2D.Double center, double angle){
double newX = center.x + Math.cos(angle) * (point.x - center.x) -
Math.sin(angle) * (point.y-center.y) ;
double newY = center.y + Math.sin(angle) * (point.x - center.x) +
Math.cos(angle) * (point.y - center.y) ;
return new Point2D.Double(newX, newY);
}
Try with
Point2D.Double point = new Point2D.Double(200,100);
Point2D.Double center = new Point2D.Double(100,100);
double angle = Math.PI/2 ; //90 degress
System.out.println(rotateAPoint(point, center, angle) );
System.out.println(rotateAPoint(point, center, -angle));
If you prefer to use double[] :
/**
* #param point (x,y) of the coordinates to be rotated
* #param center (x,y) of the center (pivot) coordinates
* #param angle in radians
* #return (x,y) of the new (translated) coordinates
*/
static double[] rotateAPoint(double[] point, double[] center, double angle){
double newX = center[0] + Math.cos(angle) * (point[0] - center[0]) -
Math.sin(angle) * (point[0]-center[0]) ;
double newY = center[1] + Math.sin(angle) * (point[1] - center[1]) +
Math.cos(angle) * (point[1] - center[1]) ;
return new double[]{newX, newY};
}
Explanation about the math here

Processing: Rotating a 3D object using Quaternions works for x-axis, but not for y or z axis?

I'm creating a Quaternion from input from a serial device. In Processing I rotate around the x-axis in the code below. My Quaternion object takes the input and uses the set function to set the values, euler angles, and normalize. Is there something wrong with the math?
I commented out rotation for z and y, but basically the object doesn't rotate around very well or is jerky compared to the x-axis, which works perfectly. What am I doing wrong in the code below?
For reference, the shape(model) line below is the loaded 3d object from a .obj file loaded in using loadShape and the shape function displays it in the draw loop.
Quaternion q = new Quaternion(s);
q.set(x, y, z, w);
q = q.Euler(q.eulerAngles);
translate(x, y);
rotateX(q.eulerAngles.x);
//rotateY(q.eulerAngles.y);
//rotateZ(q.eulerAngles.z);
shape(model);
rotateX(-q.eulerAngles.x);
translate(-x, -y);
This is part of the Quaternion class:
public class Quaternion {
PApplet s;
public float w,x,y,z;
public PVector eulerAngles;
public Quaternion(PApplet s, float x, float y, float z, float w){
this.s = s;
this.x = x;
this.y = y;
this.z = z;
this.w = w;
normalize();
}
public Quaternion(Quaternion q){
this.s = q.s;
this.w = q.w;
this.x = q.x;
this.y = q.y;
this.z = q.z;
}
public Quaternion normalize() {
float magnitude = w*w + x*x + y*y + z*z;
if(magnitude != 0.0 && magnitude != 1.0){
magnitude = 1.0f / s.sqrt(magnitude);
w *= magnitude;
x *= magnitude;
y *= magnitude;
z *= magnitude;
}
eulerAngles = setEulerAngles();
return this;
}
public Quaternion set(float x, float y, float z, float w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
return normalize();
}
// Returns a rotation that rotates z degrees around
// the z axis, x degrees around the x axis, and y
// degrees around the y axis.
public Quaternion Euler(){
float roll = eulerAngles.x;
float pitch = eulerAngles.y;
float yaw = eulerAngles.z;
float cr = (float)Math.cos(roll * 0.5);
float sr = (float)Math.sin(roll * 0.5);
float cp = (float)Math.cos(pitch * 0.5);
float sp = (float)Math.sin(pitch * 0.5);
float cy = (float)Math.cos(yaw * 0.5);
float sy = (float)Math.sin(yaw * 0.5);
w = cy*cr*cp + sy*sr*sp;
x = cy*sr*cp - sy*cr*sp;
y = cy*cr*sp + sy*sr*cp;
z = sy*cr*cp - cy*sr*sp;
return normalize();
}
// set euler angle representation of
// the rotation in 3-dim PVector
private PVector setEulerAngles(){
// roll: x-axis rotation
float sinr = 2.0f * (w*x + y*z);
float cosr = 1.0f - 2.0f * (x*x + y*y);
float roll = (float)Math.atan2(sinr, cosr);
// pitch: y-axis rotation
float sinp = 2.0f * (w*y - z*x);
float pitch = 0.0f;
if(Math.abs(sinp) >= 1){
pitch = (float)Math.copySign(Math.PI/2, sinp);
} else {
pitch = (float)Math.asin(sinp);
}
// yaw: z-axis rotation
float siny = 2.0f * (w*z + x*y);
float cosy = 1.0f - 2.0f * (y*y + z*z);
float yaw = (float)Math.atan2(siny, cosy);
return new PVector(roll, pitch, yaw);
}
}
As far as I can tell, the Euler angles that you get from your method should be applied in ZYX order rather than XYZ. But anyway, do not mess around with Euler angles unless you really have to. And in this case you don't.
Instead, convert the quaternion to a rotation matrix and apply this transform using applyMatrix(). There will be no ambiguity here.
To revert a transform, do not apply the inverse transform (like you did with rotateX(-q.eulerAngles.x) and translate(-x, -y)). It is very easy to confuse the order or forget a transform during development. Instead, use pushMatrix() / popMatrix() or resetMatrix.
Btw, I find the definition of your quaternion class very confusing. Some methods return values that I would not expect to return anything (e.g. normalize()). Furthermore, I do not think that having an Euler angle representation stored with the quaternion is a good idea. And even if you think it is, I don't understand the purpose of the method Euler() since it neither has parameters, nor can you set the Euler angles from outside.

How do I wrap a texture around a sphere?

I created a class with a method drawSphere to replace glutDrawSolidSphere. See code below.
But I wonder, how do I wrap a texture around it without tiling? For example, if I want to draw a mouth, eyes and a nose on it, then I want it to have only one mouth, two eyes and one nose, and not 100 tiled all over the sphere.
I'm using Jogl with some libraries.
class Shape {
public void drawSphere(double radius, int slices, int stacks) {
gl.glEnable(GL_TEXTURE_2D);
head.bind(gl); //This method is a shorthand equivalent of gl.glBindTexture(texture.getTarget(), texture.getTextureObject());
gl.glBegin(GL_QUADS);
double stack = (2*PI)/stacks;
double slice = (2*PI)/slices;
for (double theta = 0; theta < 2 * PI; theta += stack) {
for (double phi = 0; phi < 2 * PI; phi += slice) {
Vector p1 = getPoints(phi, theta, radius);
Vector p2 = getPoints(phi + slice, theta, radius);
Vector p3 = getPoints(phi + slice, theta + stack, radius);
Vector p4 = getPoints(phi, theta + stack, radius);
gl.glTexCoord2d(0, 0);
gl.glVertex3d(p1.x(), p1.y(), p1.z());
gl.glTexCoord2d(1, 0);
gl.glVertex3d(p2.x(), p2.y(), p2.z());
gl.glTexCoord2d(1, 1);
gl.glVertex3d(p3.x(), p3.y(), p3.z());
gl.glTexCoord2d(0, 1);
gl.glVertex3d(p4.x(), p4.y(), p4.z());
}
}
gl.glEnd();
gl.glDisable(GL_TEXTURE_2D);
}
Vector getPoints(double phi, double theta, double radius) {
double x = radius * cos(theta) * sin(phi);
double y = radius * sin(theta) * sin(phi);
double z = radius * cos(phi);
return new Vector(x, y, z);
}
}
You could just map latitude and longitude directly to the texture co-ordinates.
for (double theta = 0; theta < 2 * PI; theta += stack) {
for (double phi = 0; phi < 2 * PI; phi += slice) {
Just scale theta and phi to be between 0 and 1.
double s0 = theta / (2 * PI);
double s1 = (theta + stack) / (2 * PI);
double t0 = phi / (2 * PI);
double t1 = (phi + slice) / (2 * PI);
And use s0,s1,t0,t1 in place of 0 and 1 in your texCoord() calls.

rotating a quaternion on 1 axis?

I have a model rotated by a quaternion. I can only set the rotation, I can't add or subtract from anything. I need to get the value of an axis, and than add an angle to it (maybe a degree or radian?) and than re-add the modified quaternion.
How can I do this? (answer on each axis).
You can multiply two quaternions together to produce a third quaternion that is the result of the two rotations. Note that quaternion multiplication is not commutative, meaning order matters (if you do this in your head a few times, you can see why).
You can produce a quaternion that represents a rotation by a given angle around a particular axis with something like this (excuse the fact that it is c++, not java):
Quaternion Quaternion::create_from_axis_angle(const double &xx, const double &yy, const double &zz, const double &a)
{
// Here we calculate the sin( theta / 2) once for optimization
double factor = sin( a / 2.0 );
// Calculate the x, y and z of the quaternion
double x = xx * factor;
double y = yy * factor;
double z = zz * factor;
// Calcualte the w value by cos( theta / 2 )
double w = cos( a / 2.0 );
return Quaternion(x, y, z, w).normalize();
}
So to rotate around the x axis for example, you could create a quaternion with createFromAxisAngle(1, 0, 0, M_PI/2) and multiply it by the current rotation quaternion of your model.
Made a runable code from sje397's post for testing other details later, thought I would share. Eventually using C the reason for no Quaternion class.
#include <iostream>
#include <math.h>
using namespace std;
struct float4{
float x;
float y;
float z;
float w;
};
float4 make_float4(float x, float y, float z, float w){
float4 quat = {x,y,z,w};
return quat;
}
float dot(float4 a)
{
return (((a.x * a.x) + (a.y * a.y)) + (a.z * a.z)) + (a.w * a.w);
}
float4 normalize(float4 q)
{
float num = dot(q);
float inv = 1.0f / (sqrtf(num));
return make_float4(q.x * inv, q.y * inv, q.z * inv, q.w * inv);
}
float4 create_from_axis_angle(const float &xx, const float &yy, const float &zz, const float &a)
{
// Here we calculate the sin( theta / 2) once for optimization
float factor = sinf( a / 2.0f );
float4 quat;
// Calculate the x, y and z of the quaternion
quat.x = xx * factor;
quat.y = yy * factor;
quat.z = zz * factor;
// Calcualte the w value by cos( theta / 2 )
quat.w = cosf( a / 2.0f );
return normalize(quat);
}
int main()
{
float degrees = 10.0f;
float4 quat = create_from_axis_angle(1, 0, 0, degrees*(3.14159f/180.0f));
cout << "> (" << quat.x << ", " <<quat.y << ", " <<quat.z << ", " <<quat.w << ")" << endl;
return 0;
}
Output
(0.0871557, 0, 0, 0.996195)

Categories

Resources