JavaScript - Angular Velocity by Vector - 2D - javascript

I am making a game in JavaScript, with the Canvas API.
I perform circle to segment collision, and I need to calculate the anglar velocity for the circle by its velocity vector, I use this formula to do it:
ball.av = ball.v.length() / ball.r
// ball.av = angular velocity
// ball.v = velocity vector, contains x and y values
// .length() = returns initial velocity (pythagoras) for example: return Math.sqrt(ball.v.x * ball.v.x + ball.v.y * ball.v.y)
// ball.r = radius
Now since a square root can't be negative, this won't work when the ball is supposed to rotate anti-clockwise. So, I need a signed version of the initial velocity that also can be negative, how do I calculate that?
I've heard about that the Wedge product is working for this, and I've read many articles about it, but I still don't understand how to implement it to my code, please help!

In the general case, if the ball is rolling on a surface then the angular velocity would be the cross product of the velocity with the surface normal over the radius.
ball.av = CrossProduct(surfaceNormal, ball.v) / radius;
But if you are always on a flat surface along the x direction then this simplifies to this:
ball.av = -ball.v.x / ball.r;
Here is crossproduct for you if you don't have it.
float CrossProduct(const Vector2D & v1, const Vector2D & v2) const
{
return (v1.X*v2.Y) - (v1.Y*v2.X);
}
NOTE: if the ball rolls backwards just add a '-' sign to your calculations or swap the parameters in the crossProduct call but I think they are right as I've written them.
Surface normal is a perpendicular normalized (unit) vector from a surface. In your case surface normal is the normalized vector from the contact point to the centre of the circle.
As a side note to remove the component of gravity into a surface as a ball is rolling do this:
vec gravity;
gravity = gravity - surfaceNormal*dot(surfaceNormal, gravity);
you can then apply the resultant gravity as a ball is rolling down a surface.

Related

Trigonometry calculate tilt angle from accelerometer data

I have the following Figure and the equations:
Three Axis for measuring tilt
Equations for measuring tilt
The body on the Figures is a tri-axial accelerometer sensor, which measures accelaration in meters/seconds².
The goal is to calculate the tilt of the following angles using acceleration values:
ρ: angle of the X-axis relative to the ground (orange line);
Φ: angle of the Y-axis relative to the ground (orange line);
θ: angle of the Z-axis relative to the gravity (green line).
Could someone explain how to find equations 1,2 and 3 from the figure above?
Source of the equations and figure: https://www.thierry-lequeu.fr/data/AN3461.pdf
There is another similar and more detailed source that uses the same equations, but I also could not understand how to find them: https://www.analog.com/en/app-notes/an-1057.html
I have already implemented them and it is working, I just want help to understand how to obtain the equations. Here is the code:
let pitch = Math.atan(ax / Math.sqrt((Math.pow(ay,2) + Math.pow(az,2))) );
let roll = Math.atan(ay / Math.sqrt((Math.pow(ax,2) + Math.pow(az,2))) );
let theta = Math.atan(Math.sqrt((Math.pow(ax,2) + Math.pow(ay,2))) /az);
Thanks in advance.
This is the Pythagorean theorem, finding the 2D distance between 0,0 and a point represented by the two numbers given. If we assign that to a new function it may be clearer:
distance(a, b) { return sqrt((pow(a,2) + pow(b,2))) }
Then angles are calculated by using the inverse tangent function with a distance from that function representing one side of the triangle. For example, the pitch in your question divides the x acceleration by the distance between 0,0 and the acceleration in the YZ plane.
pitch = atan(x / distance(y, z))

Updating an intrinsic rotation with an extrinsic rotation

I am trying to model a Rubik's Cube for a personal project, using Zdog for lightweight 3d graphics. Zdog uses a {x,y,z} vector to represent rotation - I believe this is essentially a Tait-Bryan angle.
To animate a rotation of the top, right, front, etc side, I attach the 9 blocks to an anchor in the center of the cube and rotate it 90 degrees in the desired direction. This works great, but when the animation is done I need to "save" the translation and rotation on the 9 blocks. Translation is relatively simple, but I'm stuck on rotation. I basically need a function like this:
function updateRotation(xyz, axis, angle) {
// xyz is a {x,y,z} vector
// axis is "x", "y", or "z"
// rotation is the angle of rotation in radians
}
that would apply the axis/angle rotation in world coordinates to the xyz vector in object coordinates. Originally I just had xyz[axis] += angle, but this only works when no other axis has any rotation. I then thought I could use a lookup table, and I think that's possible as I only use quarter turns, but constructing the table turns out to be harder than I thought.
I am starting to suspect I need to translate the xyz vector to some other representation (matrix? quaternion?) and apply the rotation there, but I'm not sure where to start. The frustrating thing is that my blocks are in the right position at the end of the animation - I'm just not sure how to apply the parent transform so that I can detach them from the parent without losing the rotation.
As far as I can tell, this can't be done with Euler angles alone (at least not in any easy way). My solution was to convert to quaternions, rotate, then convert back to Euler:
function updateRotation(obj, axis, rotation) {
const {x, y, z} = obj.rotate;
const q = new Quaternion()
.rotateZ(z)
.rotateY(y)
.rotateX(x);
const q2 = new Quaternion();
if (axis === 'x') {
q2.rotateX(rotation);
} else if (axis === 'y') {
q2.rotateY(rotation);
} else if (axis === 'z') {
q2.rotateZ(rotation);
}
q.multiply(q2, null);
const e = new Euler().fromQuaternion(q);
obj.rotate.x = e.x;
obj.rotate.y = e.y;
obj.rotate.z = e.z;
obj.normalizeRotate();
}
This uses the Euler and Quaternion classes from math.gl.
(It turned out Zdog actually uses ZYX Euler angles as far as I could tell, hence the order of rotations when creating the first quaternion.)

Calculating a quaternion from two combined angles

I'm creating a script that rotates a THREE.js camera arround based on a mobile phones gyroscope input. It's currently working pretty well, except that every time I rotate my phone over a quadrant, the camera will turn 180 degrees instead of continuing as intended. This is the code that I currently use:
private onDeviceOrientation = ( event ) => {
if( event.alpha !== null && event.beta !== null && event.gamma !== null ) {
let rotation = [
event.beta,
event.alpha,
event.gamma
],
this.orientation = new THREE.Vector3(rotation[0], rotation[1], rotation[2]);
this.viewer.navigation.setTarget(this.calcPosition());
}
};
private calcPosition = () => {
const camPosition = this.viewer.navigation.getPosition(),
radians = Math.PI / 180,
aAngle = radians * - this.orientation.y,
bAngle = radians * + this.orientation.z,
distance = this.calcDistance();
let medianX = Math.cos(bAngle) * Math.sin(aAngle);
let medianY = Math.cos(bAngle) * Math.cos(aAngle);
let nX = camPosition.x + (medianX * distance),
nY = camPosition.y + (medianY * distance),
nZ = camPosition.z + Math.sin(bAngle) * distance;
return new THREE.Vector3(nX, nY, nZ);
};
window.addEventListener('deviceorientation', this.onDeviceOrientation, false);
Soafter doing some research I found that I need to use a Quaternion prevent the switchen when going into a new quadrant. I have no experience with Quaternions, so I was wondering what the best way would be to combine the two Vector3's in the code above into a singel Quaternion.
[Edit]
I calculate the distance using this method:
private calcDistance = (): number => {
const camPosition = this.viewer.navigation.getPosition();
const curTarget = this.viewer.navigation.getTarget();
let nX = camPosition.x - curTarget.x,
nY = camPosition.y - curTarget.y,
nZ = camPosition.z - curTarget.z;
return Math.sqrt((nX * nX) + (nY * nY) + (nZ * nZ));from squared averages
};
And I follow the MDN conventions when working with the gyroscope.
[Edit #2]
Turns out I had my angle all wrong, I managed to fix it by calculating the final position like this:
let nX = camPosition.x - (Math.cos(zAngle) * Math.sin(yAngle)) * distance,
nY = camPosition.y + (Math.cos(zAngle) * Math.cos(yAngle)) * distance,
nZ = camPosition.z - (Math.cos(xAngle) * Math.sin(zAngle)) * distance;
Here is the closest I can give you to an answer:
First of all, you don't need a quaternion. (If you really find yourself needing to convert between Euler angles and quaternions, it is possible as long as you have all the axis conventions down pat.) The Euler angle orientation information you obtain from the device is sufficient to represent any rotation without ambiguity; if you were calculating angular velocities, I'd agree that you want to avoid Euler angles since there are some orientations in which the rates of change of the Euler angles go to infinity. But you're not, so you don't need it.
I'm going to try to summarize the underlying problem you're trying to solve, and then tell you why it might not be solvable. 🙁
You are given the full orientation of the device with a camera, as yaw, pitch, and roll. Assuming yaw is like panning the camera horizontally, and pitch is like tilting the camera vertically, then roll is a degree of freedom that doesn't change affect direction the camera is pointing, but it does affect the orientation of the images the camera sees. So you are given three coordinates, where two have to do with the direction the camera is pointing, and one does not.
You are trying to output this information to the camera controller but you are only allowed to specify the target location, which is the point in space that the camera is looking. This is to be specified via three Cartesian coordinates, which you can calculate from the direction the camera is pointing (2 degrees of freedom) and the distance to the target object (one degree of freedom).
So you have three inputs and three outputs, but only two of those have anything to do with each other. The target location has no way to represent the roll direction of the camera, and the orientation of the camera has no way to represent the distance to some target object.
Since you don't have a real target object, you can just pick an arbitrary fixed distance (1, for example) and use it. You certainly don't have anything from which to calculate it... if I follow your code, you are defining distance in terms of the target location, which is itself defined in terms of the distance from the previous step. This is extra work for no benefit at best (the distance drifts around some initial value), and numerically unstable at worst (the distance drifts toward zero and you lose precision or get infinities). Just use a fixed value for distance and make it simple.
So now you probably have a system that points a camera in a direction, but you cannot tell it what the roll angle is. That means your camera controller is apparently just going to choose it for you based on the yaw and pitch angles. Let's say it always picks zero degrees (that would be the least crazy thing it could do). This will cause discontinuities when the roll angle and yaw angle line up (when the pitch is at ±90°): Imagine pointing a physical camera at the northern horizon and yawing around westward, past the western horizon, and settling on the southern horizon. The whole time, the roll angle of the camera is 0°, so there's no problem. But now imagine pointing it at the northern horizon, and pitching upward, past the zenith, and continuing to pitch backward until you are facing the southern horizon. Now the camera is upside down; the roll angle is 180°. But if the camera controller doesn't change the roll angle from 0°, then it will do a nonphysical "flip" right when you pass the zenith. The problem is that there really is no way to synthesize a roll angle based purely on position and not have this happen. We've just demonstrated that there are two ways to move your camera from pointing north to pointing south, where the roll angle is completely different at the end.
So you're stuck, I guess. Well, maybe not. Can you rotate the image from the camera based on the roll angle of the device orientation? That is, add the roll back into the displayed image? If so, you may have a solution. Let's say the roll angle of the camera controller is always at zero. Then you just rotate the image by the desired roll angle (something derived from beta I guess?) and you're done. If the camera controller has some other convention for choosing the roll angle, you will need to figure that out, undo it, and add the roll angle back on.
Without the actual system in front of me I probably can't help you debug your way to a solution. So I think this is where my journey with this question must end. Good luck!
Summary:
You don't need a quaternion
Pick a fixed distance to your simulated target
Add the roll angle by rotating the image before displaying it
Good luck!

What's the significance of 1/cos(x) in this code for a 3d canvas game?

I've been experimenting with HTML5 canvases lately and came across this 3d example with relatively little code behind it. I was hoping to find a good introduction to 3d rendering, but I'm having more trouble understanding the geometry behind the code than I was expecting to. I set up a JSbin and copied over the code that was used on his website to play with. I'm stuck at understanding the meaning of
deltaX=1/Math.cos(theta);
which is later used in:
if (deltaX>0) {
stepX = 1;
distX = (mapX + 1 - x) * deltaX;
}
else {
stepX = -1;
distX = (x - mapX) * (deltaX*=-1);
}
Source
My best guess is that it's used for the relation cos(x) = adjacent/hypotenuse in a right triangle, but I don't understand where the triangle would fit in, if at all.
If you draw a line from the origin (0, 0) with direction theta (measured from the x-axis), then
deltaX = 1/cos(theta) is the distance on this line until the vertical line x = 1 is met, and
deltaY = 1/sin(theta) is the distance on this line until the horizontal line y = 1 is met.
It is indeed a triangle relation. In the first case, the triangle has the points (0, 0), (1, 0) and the point (1, y) where the line meets the vertical line x=1.
(mapX, mapY) is a grid point with integer coordinates, and (x, y) is a point in the square [mapX, mapX+1) x [mapY, mapY+1).
distX computes the distance of the next vertical grid line in theta-direction, and distY the distance of the next horizontal grid line.
Remark: The computation fails if the direction is a multiple of π/2, i.e. the direction is exactly right, up, left, or down, because sin(theta) = 0 or cos(theta) = 0 in that case. This probably does not happen in your program, because the playerDirection starts with 0.4 and is incremented or decremented by 0.07.

How do I calculate the rotation along the y-axis given a 2D direction vector?

I'm making a 3D game, and I need the player mesh always facing the back of the camera. I already figured out how to get a 2D speed vector (direction along the x-z plane), but now I need to rotate the mesh in the speed vector's direction...
Basically, every mesh has a .rotation property, and that property is a 3D vector. I am only interested in rotation over the y-axis, that's the one that is perpendicular to the surface (x-z) plane.
The rotation doesn't use degrees, but radians, so I thought it would be something like this:
mesh.rotation.y = (mesh.direction.x - mesh.direction.z)*Math.PI*2;
But this doesn't seem to cut it...
The direction/speed is a, as a said, 2D vector, and it consist of real numbers between -1 and 1. At all times sqrt(x*x + y*y) == 1, so it forms a "circle", this is because speed needs to be equal in all directions, obviously.
The speed vector changes only when I drag the mouse over the screen, and so should the rotation, and it is calculated like this:
var c = Math.sqrt(cameraPos.x*cameraPos.x + cameraPos.z*cameraPos.z); //This is the distance from the camera to the mesh, which is at (0, 0) for simplicity of this presentation.
var rat = 1/c;
mesh.direction.x = cameraPos.x*rat; //Direction vector = the speed vector
mesh.direction.z = cameraPos.z*rat;
If I understand correctly, atan2 will do the trick:
mesh.rotation.y = Math.atan2(mesh.direction.z, mesh.direction.x)
Result is in radians. It basically calculates the angle between the vector and X axis. You might need to switch parameters or use minus operator here or there.

Categories