Wiimote Project
Wiimote Projects => Wiimote Desktop VR/Head Tracking => Topic started by: tarantula78 on February 26, 2008, 03:00:13 PM

Hi all,
Im pretty average at maths but there was one part of the code that I didnt understand and that is how the distance from the TV/Monitor is calculated.
I gathered that this part of the code was relied on:
Line 261:
double angle = Math.Acos(.5 / headDist)Math.PI / 2;//angle of head to screen
Line 777:
headDist = movementScaling * (float)((dotDistanceInMM / 2) / Math.Tan(angle)) / screenHeightinMM;
If anyone could explain why it is 0.5 that is being divided and how it is calculated.
Thanks

ok, sincere apologies to everyone thats not the code i needed help with its the wrong one >.<
float angle = radiansPerPixel * pointDist / 2;
//in units of screen hieght since the box is a unit cube and box hieght is 1
headDist = movementScaling * (float)((dotDistanceInMM / 2) / Math.Tan(angle)) / screenHeightinMM;
float avgX = (firstPoint.x + secondPoint.x) / 2.0f;
float avgY = (firstPoint.y + secondPoint.y) / 2.0f;
//should calaculate based on distance
headX = (float)(movementScaling * Math.Sin(radiansPerPixel * (avgX  512)) * headDist);
relativeVerticalAngle = (avgY  384) * radiansPerPixel;//relative angle to camera axis
if(cameraIsAboveScreen)
headY = .5f+(float)(movementScaling * Math.Sin(relativeVerticalAngle + cameraVerticaleAngle) *headDist);
else
headY = .5f + (float)(movementScaling * Math.Sin(relativeVerticalAngle + cameraVerticaleAngle) * headDist);
}
the lines i need explained are highlighted
in the first line: why is it divided by 2? is it because the triangle that is formed is divided to make a right angle triangle?
the second line: what is the significance of dividing the dot distance with the Tangent of the angle retrieved?
in the third and fourth could someone just give me a general explanation of whats going on.
Thanks in advance.
Tarantula

Your right about 1), and that leads into 2)
(http://www.wilco.ismysite.co.uk/Geometry.GIF)
we know the actual length of the top side, as it dotDistanceInMM.
From the first line we know the angle (each pixel on the camera in the wiimote represents a set amount of angle).
This is a isoceles triangle, so dividing both by 2 gives us a right angle triangle, the white area in the picture. Pythagorean rules for right angle triangles define
Tan(angle) = opposite / adjacent
or in our variable terms
Tan(angle) = (dotDistanceInMM/2) / headDist
adjacent is what we want to find (headDist), so rearranging:
headDist = (dotDistanceInMM/2) / Tan(angle)
the other 2 terms (confusingly 1 at the start and 1 at the end) are just scaling factors, the first allows use to exaggerate all movement if we want to, and the second normalises by the size of the screen. Not totally sure about this bit, it means if you have a large screen everything seems bigger, bit if you have your head in the same place you see the same thing (as opposed to be being able to see more)
3) combines a few operations into 1 line of code. So working outwards from the middle:
avgX is the midpoint of the head from the far left of the camera
avg512 is the midpoint of the head, with 0 being right in the centre of the screen. ve 1 side and +ve the other.
radiansPerPixel * (avgX  512) = the angle of the centre of the head from the centerline of the camera, so again ve angle is on 1 side and +ve on the other.
Now we construct a very similar triangle to last time:
(http://www.wilco.ismysite.co.uk/Geometry2.GIF)
where we know headDist, and angle, and want to headX.
Sin(angle) = opposite / hypotenuse
Sin(radiansPerPixel * (avgX  512)) = headX / headDist
rearranging:
headX = Sin(radiansPerPixel * (avgX  512)) * headDist
again add in the movementScaling term to allow exaggeration.
headY requires a bit more, as we dont assume the camera is horizontal (but we did assume it was perpedicular to the screen for headX).Also it needs to take into account being above or below the screen (again for X we assumed the wiimote was in the middle of the screen).
so we add on cameraVerticaleAngle (nicely misspelt).
and then add or subtract 0.5 depending on where the camera is.
Why 0.5? because ever since we "/screenHeightinMM" in 2) we have been work in terms of screen heights. so headX would say "head is 1 screen height left" and headY would say "head is 0.7 screen heights up". So if the camera is half a screen height above the TV we need to subtract 0.5 to move to the middle of the TV.
so From this we have an X,Y,Z (headX,headY,headDist) coordinate of the head relative to the dead centre of the TV screen.

Hi!
To Wilco:
Thanks man! That explanation really helped!
But I have querstion too. If you have the sketch of Horizontal and Vertical Position in front of you, would see
that headDist is different in two positions but why they are used as being similar! I guessit is a mistake unless I am ignoring something! The relation is as below:
headDist_Y^2=headDist_X^2+DheadY^2 or similarly
tan(teta_headY)=DheadY/headDist_Y where headDist_Y is not what was previously calculated as headDist_X;
Can you help me?!

By the way, when the camera is above the acreen 0.5f is added not subtracted!?You know why!?
if(cameraIsAboveScreen)
headY = .5f+(float)(movementScaling * Math.Sin(relativeVerticalAngle + cameraVerticaleAngle) *headDist);

Thanks from me too for that, helped a lot.
I have a question, early in that maths Johnny calculates the distance between the two dots as follows (code is c++ and using a different library so will be different to Johnny's but the maths is the same):
float dx = firstPoint>GetX()  secondPoint>GetX();
float dy = firstPoint>GetY()  secondPoint>GetY();
float pointDist = (float)sqrt(dx * dx + dy * dy);
This gives us the distance between the two points.
Why then, do we also need to manually give the width of our sensor bar in the form of dotDistanceInMM?
In addition, why is avgX  512 the midpoint of the head? Is it because the camera is 1024 wide?
Cheers

In addition, in your diagrams you show headDist as at one stage being the Adjacent and in the next, the Hypotenuse... this isn't right surely? The value of headDist isn't changed between these two diagrams so how can the one value possibly be used to represent two different length sides?
Thanks

Cheers for the explanation, cleared things up a bit. I actually went ahead and did things differently and worked out the head distance based on the fact you know how wide the sensor bar is in reality, and you know how wide it looks to the wiimote camera, so from that you can work out how far away it is, using the inverse of 3D projection.
Whereas normally you would have:
point2D.x = (znear*point3D.x)/point3D.z;
point2D.y = (znear*point3D.y)/point3D.z;
Solving for z gives:
z = (znear*x3D)/x2D;
// or in this case
z = (znear*widthReal)/widthOnCamera;
It actually didn't occur to me to do the radians per pixel, thinking about it that's a much more logical idea.. There's something about messing with 3D to 2D projections (and vice versa) which instantly makes me start picturing a buttload of right angle triangles!

hey people :)
I'm sorry that I'm taking this old topic up again.
But I still don't get this:
if(cameraIsAboveScreen)
headY = .5f+(float)(movementScaling * Math.Sin(relativeVerticalAngle + cameraVerticaleAngle) *headDist);
else
headY = .5f + (float)(movementScaling * Math.Sin(relativeVerticalAngle + cameraVerticaleAngle) * headDist);
}
and then add or subtract 0.5 depending on where the camera is.
Why 0.5? because ever since we "/screenHeightinMM" in 2) we have been work in terms of screen heights. so headX would say "head is 1 screen height left" and headY would say "head is 0.7 screen heights up". So if the camera is half a screen height above the TV we need to subtract 0.5 to move to the middle of the TV.
But the opposite is done: if the camera is above screen 0.5 is added. Why this? I'd be very happy if someone could explain that to me ;)
Greets