Double Quaternion

Is it possible to create a Quaternion that uses doubles instead of floats? I find if I rotate a point, say (10, 10, 10) by some angle and then back again, I get something very close, but not accurate, like (9.955, 10.121, 9.802). It seems the floats are rounding numbers or something, and I feel like it would work better if it was using doubles. Thanks!

That’s a lot of difference for rounding errors so I don’t think that’s what it is. More likely that the order of angles is screwing you up.

The three angles (Euler angles) are applied in a specific order. If you apply (10, 10, 10) and then (-10, -10, -10) you will get different results because the order will not be reversed in the second case.

You’d have to show us the code to be sure.

If you want to test it, instead of applying all three angles at the same time, apply them one at a time and then do the negative angles applied in the reverse order. The result should be so close to the original so as not to matter at all.

Well this is essentially what I am doing:

[java]
Quaternion q = Node.getLocalRotation().inverse();
Vector3f rotatedCollisionPoint = q.mult(collisionContactPoint);
[/java]

Basically I have a bunch of cubes formed into a “planet” (a bigger cube… I’m using the cubes library.) which is rotating. I’m trying to find what block to delete when I click it, so I have to “unrotate” the point to find where the point would have been had the planet not been rotated. It needs to be precise because the cubes library knows which face of the block to delete based on which number is divisible by 3 with no remainder. When I unrotate the point, I get a number close to a whole number but not quite. That’s the issue. Could this be caused by the order not being reversed as you said? Thanks

Edit: Here are the results of a point which should be at -72, -72, -72 after “unrotating:”
Quaternion: (-0.0, -0.79467344, -0.0, 0.6070277) - Unrotated Point: (-71.99882, -72.00015, -72.00107)
Any way to get those as whole numbers? (without rounding)

You won’t get whole numbers even with doubles - just error will be smaller. You always have to perform some kind of rounding/epsilon comparison when working with floating point numbers.

<cite>@abies said:</cite> You won't get whole numbers even with doubles - just error will be smaller. You _always_ have to perform some kind of rounding/epsilon comparison when working with floating point numbers.

I see. Thanks for the help all. I spent a while playing with equations on a whiteboard and I think I got a solution to round to the right results. Thanks again!

@PewPewKapowie said: Any way to get those as whole numbers? (without rounding)

As you’ve seen the answer is kind of “no”… but why do you want to avoid rounding them?

<cite>@pspeed said:</cite> As you've seen the answer is kind of "no"... but why do you want to avoid rounding them?

Well, with the cubes library, when you click a block, the axis parallel to the first face the ray hits is always at a number which is divisible by the width of the block. So for example, if I had a block that was 3x3x3 and I clicked one of its faces, I might get a coordinate (13.5435, 48.0, 50.3235). One number is always whole. This helps when determining which side of the block was clicked, as shown by this code: (this is the code from the cubes library)

[java]public static Vector3Int getPointedBlockLocation(BlockTerrainControl blockTerrain, Vector3f collisionContactPoint, boolean getNeighborLocation){
Vector3f collisionLocation = Util.compensateFloatRoundingErrors(collisionContactPoint);
Vector3Int blockLocation = new Vector3Int(
(int) (collisionLocation.getX() / blockTerrain.getSettings().getBlockSize()),
(int) (collisionLocation.getY() / blockTerrain.getSettings().getBlockSize()),
(int) (collisionLocation.getZ() / blockTerrain.getSettings().getBlockSize()));
if((blockTerrain.getBlock(blockLocation) != null) == getNeighborLocation){
if((collisionLocation.getX() % blockTerrain.getSettings().getBlockSize()) == 0) {
blockLocation.subtractLocal(1, 0, 0);
}
else if((collisionLocation.getY() % blockTerrain.getSettings().getBlockSize()) == 0){
blockLocation.subtractLocal(0, 1, 0);
}
else if((collisionLocation.getZ() % blockTerrain.getSettings().getBlockSize()) == 0){
blockLocation.subtractLocal(0, 0, 1);
}
}
return blockLocation;
}[/java]

You can see how it determines which side is divisible by the block size, in our case 3. This is how it knows what side was clicked. When I unrotate the collision point, the number is no longer whole, and this doesn’t work. On top of that, if I round the number, sometimes I get two numbers which are divisible by 3, and sometimes I get the block just one block over from the one I clicked. This wouldn’t happen when the cube isn’t rotated because two of the axes have floating points. When you round, all of them are whole numbers.

So I went and played on the whiteboard and came up with this “Smart Rounder.” It determines which axis is closest to a number divisible by 3, and only rounds that axis. Though it may be very inefficient coding (and I am sure there are ways to make it smaller/faster), so far I have had no issues with it. :slight_smile:

[java]private static Vector3f smartRound(Vector3f vec) {

	float x = vec.x;
	float y = vec.y;
	float z = vec.z;

	String xs = new String(x+"");
	String ys = new String(y+"");
	String zs = new String(z+"");
	System.out.println(xs+" "+ys+" "+zs);
	float xd = Float.parseFloat("0."+xs.substring(xs.indexOf(".")+1, xs.length()));
	float yd = Float.parseFloat("0."+ys.substring(ys.indexOf(".")+1, ys.length()));
	float zd = Float.parseFloat("0."+zs.substring(zs.indexOf(".")+1, zs.length()));

	System.out.println(xd+" "+yd+" "+zd);
	
	Vector3f newvec = vec.clone();
	int greatest = 0, smallest = 0;

	if((xd &gt; yd &amp;&amp; xd &gt; zd))
	{
		greatest=0;
	}
	else if((yd &gt; xd &amp;&amp; yd &gt; zd))
	{
		greatest=1;
	}
	else if((zd &gt; xd &amp;&amp; zd &gt; yd))
	{
		greatest=2;
	}
	
	if((xd &lt; yd &amp;&amp; xd &lt; zd))
	{
		smallest=0;
	}
	else if((yd &lt; xd &amp;&amp; yd &lt; zd))
	{
		smallest=1;
	}
	else if((zd &lt; xd &amp;&amp; zd &lt; yd))
	{
		smallest=2;
	}
	
	if(greatest == 0 &amp;&amp; smallest == 1) //xy
	{
		int ground = StrictMath.round(x);
		int sround = StrictMath.round(y);
		
		if(ground % 3 == 0 &amp;&amp; sround % 3 == 0)
			if(Math.abs(x-ground) &lt; Math.abs(y-sround))
				newvec = new Vector3f(ground, y, z);
			else
				newvec = new Vector3f(x, sround, z);
		else if(ground % 3 == 0)
			newvec = new Vector3f(ground, y, z);
		else
			newvec = new Vector3f(x, sround, z);
	}
	else if(greatest == 0 &amp;&amp; smallest == 2) //xz
	{
		int ground = StrictMath.round(x);
		int sround = StrictMath.round(z);
		
		if(ground % 3 == 0 &amp;&amp; sround % 3 == 0)
			if(Math.abs(x-ground) &lt; Math.abs(z-sround))
				newvec = new Vector3f(ground, y, z);
			else
				newvec = new Vector3f(x, y, sround);
		else if(ground % 3 == 0)
			newvec = new Vector3f(ground, y, z);
		else
			newvec = new Vector3f(x, y, sround);
	}
	else if(greatest == 1 &amp;&amp; smallest == 0) //yx
	{
		int ground = StrictMath.round(y);
		int sround = StrictMath.round(x);
		
		if(ground % 3 == 0 &amp;&amp; sround % 3 == 0)
			if(Math.abs(y-ground) &lt; Math.abs(x-sround))
				newvec = new Vector3f(x, ground, z);
			else
				newvec = new Vector3f(sround, y, z);
		else if(ground % 3 == 0)
			newvec = new Vector3f(x, ground, z);
		else
			newvec = new Vector3f(sround, y, z);
	}
	else if(greatest == 1 &amp;&amp; smallest == 2) //yz
	{
		int ground = StrictMath.round(y);
		int sround = StrictMath.round(z);
		
		if(ground % 3 == 0 &amp;&amp; sround % 3 == 0)
			if(Math.abs(y-ground) &lt; Math.abs(z-sround))
				newvec = new Vector3f(x, ground, z);
			else
				newvec = new Vector3f(x, y, sround);
		else if(ground % 3 == 0)
			newvec = new Vector3f(x, ground, z);
		else
			newvec = new Vector3f(x, y, sround);
	}
	else if(greatest == 2 &amp;&amp; smallest == 0) //zx
	{
		int ground = StrictMath.round(z);
		int sround = StrictMath.round(x);
		
		if(ground % 3 == 0 &amp;&amp; sround % 3 == 0)
			if(Math.abs(z-ground) &lt; Math.abs(x-sround))
				newvec = new Vector3f(x, y, ground);
			else
				newvec = new Vector3f(sround, y, z);
		else if(ground % 3 == 0)
			newvec = new Vector3f(x, y, ground);
		else
			newvec = new Vector3f(sround, y, z);
	}
	else if(greatest == 2 &amp;&amp; smallest == 1) //zy
	{
		int ground = StrictMath.round(z);
		int sround = StrictMath.round(y);
		
		if(ground % 3 == 0 &amp;&amp; sround % 3 == 0)
			if(Math.abs(z-ground) &lt; Math.abs(y-sround))
				newvec = new Vector3f(x, y, ground);
			else
				newvec = new Vector3f(x, sround, z);
		else if(ground % 3 == 0)
			newvec = new Vector3f(x, y, ground);
		else
			newvec = new Vector3f(x, sround, z);
	}
	
	return newvec;
}[/java]

That string thing there was a cheap way to get the decimal from the number. I tried (n % 1) and (n - (int) n) but it kept giving me weird numbers and incorrect results. Maybe I did something wrong, I dunno. This works though.

Thanks!

Regarding decimal part, try
[java]
x - Math.floor(x)
[/java]
It should give you what you expect.

@abies said: Regarding decimal part, try [java] x - Math.floor(x) [/java] It should give you what you expect.

Heheh… was going to suggest the same thing.

@PewPewKapowie said: [java] float x = vec.x; float y = vec.y; float z = vec.z;
	String xs = new String(x+"");
	String ys = new String(y+"");
	String zs = new String(z+"");
	System.out.println(xs+" "+ys+" "+zs);
	float xd = Float.parseFloat("0."+xs.substring(xs.indexOf(".")+1, xs.length()));
	float yd = Float.parseFloat("0."+ys.substring(ys.indexOf(".")+1, ys.length()));
	float zd = Float.parseFloat("0."+zs.substring(zs.indexOf(".")+1, zs.length()));

[/java]

Heheh… I think somewhere an angel just lost its wings. :wink: