Find translation of point on grid using angle and distance of motion - math

I am attempting to create an in-game projectile that moves on a grid at a certain translation per second, but to do this I need equations to find the translation of the projectile in the two orthogonal directions using the angle of motion and the distance per second (speed).

Break the speed u into ux and uy using trigonometry.
S = U * time;
Sx = S * cos ( theta )
Sy = S * sin ( theta )

Related

3D: avoid pinching at poles when creating sphere from polar coordinates

I'm using Wikipedia's spherical coordinate system article to create a sphere made out of particles in Three.js. Based on this article, I created a small Polarizer class that takes in polar coordinates with setPolar(rho, theta, phi) and it returns its corresponding x, y, z
Here's the setPolar() function:
// Rho: radius
// theta θ: polar angle on Y axis
// phi φ: azimuthal angle on Z axis
Polarizer.prototype.setPolar = function(rho, theta, phi){
// Limit values to zero
this.rho = Math.max(0, rho);
this.theta = Math.max(0, theta);
this.phi = Math.max(0, phi);
// Calculate x,y,z
this.x = this.rho * Math.sin(this.theta) * Math.sin(this.phi);
this.y = this.rho * Math.cos(this.theta);
this.z = this.rho * Math.sin(this.theta) * Math.cos(this.phi);
return this;
}
I'm using it to position my particles as follows:
var tempPolarizer = new Polarizer();
for(var i = 0; i < geometry.vertices.length; i++){
tempPolarizer.setPolar(
50, // Radius of 50
Math.random() * Math.PI, // Theta ranges from 0 - PI
Math.random() * 2 * Math.PI // Phi ranges from 0 - 2PI
);
// Set new vertex positions
geometry.vertices[i].set(
tempPolarizer.x,
tempPolarizer.y,
tempPolarizer.z
);
}
It works wonderfully, except that I'm getting high particle densities, or "pinching" at the poles:
I'm stumped as to how to avoid this from happening. I thought of passing a weighted random number to the latitude, but I'm hoping to animate the particles without the longitude also slowing down and bunching up at the poles.
Is there a different formula to generate a sphere where the poles don't get as much weight? Should I be using quaternions instead?
For random uniform sampling
use random point in unit cube , handle it as vector and set its length to radius of your sphere. For example something like this in C++:
x = 2.0*Random()-1.0;
y = 2.0*Random()-1.0;
z = 2.0*Random()-1.0;
m=r/sqrt(x*x+y*y+z*z);
x*=m;
y*=m;
z*=m;
where Random return number in <0.0,1.0>. For more info see:
Procedural generation of stars with skybox
For uniform non-random sampling
see related QAs:
Sphere triangulation by mesh subdivision
Make a sphere with equidistant vertices
In order to avoid high density at the poles, I had to lower the likelihood of theta (latitude) landing close to 0 and PI. My input of
Math.random() * Math.PI, for theta gives an equal likelihood to all values (orange).
Math.acos((Math.random() * 2) - 1) perfectly weights the output to make 0 and PI less likely along the sphere's surface (yellow)
Now I can't even tell where the poles are!

can I find the sine value of a cosine value without calculating the angle?

The magnitude of the cross product describes the signed area of the parallelogram described by the two vectors (u, v) used to build the cross product, it has its uses. This same magnitude can be calculated as the magnitude of u times the magnitude of v times the sine of the angle between u and v:
||u||||v||sin(theta).
Now the dot product of u (normalized) and v (normalized) gives the cosine of the angle between u and v:
cos(theta)==dot(normalize(u), normalize(v))
I want to be able to get the signed sine value that is related to the cosine value. It is related because the sine and cosine waves are PI/2 out of sync. I know that the square root of 1 less the cosine value squared gives the unsigned sine value:
sin(theta)==sqrt(1 - (cos(theta) * cos(theta))
Where by cos(theta) I mean the dot product not the angle.
But the attendant sign calculation (+/-) requires theta as an angle:
(cos(theta + PI / 2)) > or == or < 0
If I have to perform an acos function I might as well just do the cross product and find the magnitude.
Is there a known ratio or step that can be added to a cosine value to get its related sine value?
For each possible cosine, both signs are possible for the sine if the corresponding angle is unrestricted.
If you know the angle is between [0,pi], then the sine must be positive or zero.
If you want to know the area of a parallelogram, always take the positive branch sin(x) = sqrt(1 - cos(x)^2). Negative area rarely makes sense (only to define orientation w.r.t. to a plane such as for backface culling)
If you have the two vectors, use a cross product or dot product directly, not the other one and convert.
Seems to me like a complicated way to get to atan2 identities:
d = 𝐚·𝐛 = |𝐚||𝐛|cosθ
c = |𝐚×𝐛| = |𝐚||𝐛|sinθ (with 0° < θ < 180°)
tanθ = 𝐚·𝐛 / |𝐚×𝐛|
θ = atan2(c·sgn(c|z), d) (= four quadrant)
where sgn(c|z) is the sign of the z-component in c (unless 𝐚 and 𝐛 both run exactly parallel with the xz or yz plane, then its the sign of the y-component and x-component, respectively).
Now, from basic trig identities,
r = √(x²+y²)
cos(atan2(y,x)) = x/r
sin(atan2(y,x)) = y/r
Therefore,
sinθ = c·sgn(c|z)/√(c²+d²)
cosθ = d/√(c²+d²)
I think I have found a solution.
cos(b) == sin(a)
v_parallel = dot(normalize(u), v) // the projection of v on u
v_perp = normalize(v) - v_parallel
cos(b) = dot(normalize(v), v_perp) // v_perp is already normalized
Therefore, the magnitude of
u cross v = magnitude(u) * magnitude(v) * cos(b)

Limit camera pitch

How can I efficiently limit camera pitch when I have only camera quaternion? Do I have to convert to euler angles and then back to quaternion or is there any other way?
If the camera never has any roll (as is common in lots of games, such as first person shooters), then the solution is simple. If there is roll, then there's an additional step involved. I'll start with what to do if there is no roll, and generalize the solution to what to do if there is.
Let qc be the camera rotation. Let qy a rotation with the same yaw as qc, but with zero pitch. If there is no roll, the camera rotation is a yaw rotation followed by a pitch rotation:
qc = qp * qy
We can recover the pitch rotation qp as the rotation from qy to qc:
qp = qc * qy^-1
The trick, then, is to construct qy, so we can plug it into the above equation to solve for qp. Let vc be the unit vector pointing out of the lens of the camera, or the "forward vector". Let vy be the same vector, but projected to the horizontal plane and normalized. Finally, let v0 be the forward vector when the camera rotation qc is the identity rotation. The rotation that rotates v0 into vy is the yaw rotation. The angle can be given as:
yaw = asin(Norm(cross(v0, vy)))
The corresponding yaw rotation is:
qy = { cos(yaw/2), up * sin(yaw/2) }
Where "up" is the unit vector in the up direction, aka the axis for yaw rotations. Plug this into qp = qy^-1 * qc above to get pitch quaternion qp. Finally, get the pitch angle from qp as:
pitch = 2*asin(Dot(right, [qp[1], qp[2], qp[3]]))
Where "right" is the unit vector in the right direction, aka the axis for pitch rotations.
Like I said, things get more complicated if the camera also has roll, but the general strategy is the same. You formulate the camera rotation as a product of rotation components, then isolate the component you want (in this case, pitch). For example, if the euler sequence you use to define "pitch" is the common yaw-pitch-roll sequence, you define qc as:
qc = qr * qp * qy
We can define a variable qx to be the combined pitch and roll rotations:
qx = qr * qp
We can now write qc as:
qc = qx * qy
We already know how to solve for qx in this form, by retracing the steps we used above to solve for qp. Rearranging the definition for qx, we get:
qp = qr^-1 * qx
We just solved for qx, so to solve for the pitch rotation qp, we only need the roll qr. We can construct it using vectors as we did previously. Let vc be the forward vector again. The roll will be a rotation around this vector. Let vu be the camera's up vector (in world coordinates), and let vu0 be the camera's up vector with zero roll. We can construct vu0 by projecting the global up vector to the plane perpendicular to vc, then normalizing. The roll rotation qr is then the rotation from vu0 to vu. The axis of this rotation is the forward vector vc. The roll angle is
roll = asin(Dot(vc, cross(vu0, vu)))
The corresponding quaternion is:
qr = { cos(roll/2), forward * sin(roll/2) }
Where "forward" is the axis of roll rotations.
The pitch is just one component of the full rotation, so if you want to think about your rotation like that, you'd better store the pitch separately, possibly using Euler angles.
Just converting to Euler angles and back when you need to limit the movement might not work too well, since you'l need to remember the last frame (if you have any) to see if you passed the limit, and in what direction.
As I see it, the main point of quaternions is that you don't need to bother with limits like that. Everything just works without any singularities. Why exactly do you want to limit the pitch?
Camera rotation quaternions can be defined as:
vector A = [x, y, z]
Q.x = A.x * sin(theta/2)
Q.y = A.y * sin(theta/2)
Q.z = A.z * sin(theta/2)
Q.w = cos(theta/2)
Where A is the position and theta is the angle you want to rotate the camera (adjust the pitch).
So if you have the quaternion available you can limit the angle of pitch by verifying each time if the rotation angle plus/minus the actual angle is ok.
I think you can avoid conversion if you set your limits as
+cos(supLim/2) < (Q.w + P.w) < -cos(infLim/2)
As cosine is an continuous function this is supposed to work.
If you could post the code you're actually using maybe we can help a little more.
I may be a little late to the party, but this is how I solved it:
// "Up" = local vector -> rotation * Vector3.UnitY
// "Forward" = local vector -> rotation * Vector3.UnitZ
// "Right" = local vector -> rotation * Vector3.UnitX
public void Rotate(Vector3 axis, float angle)
{
if (LerpRotation)
{
RotationTarget *= Quaternion.FromAxisAngle(axis, angle);
}
else
{
Rotation *= Quaternion.FromAxisAngle(axis, angle);
}
//Locking the Pitch in 180°
float a = Vector3.CalculateAngle(Vector3.UnitY, Up);
float sign = Math.Sign(Forward.Y);
float delta = (float)Math.PI / 2 - a;
if(delta < 0)
Rotation *= Quaternion.FromAxisAngle(Right, delta * sign);
}

Vector deltas and moving in unknown areas

I was in need of a little math help that I can't seem to find the answer to, any links to documentation would be greatly appreciated.
Heres my situation, I have no idea where I am in this maze, but I need to move around and find my way back to the start. I was thinking of implementing a waypoint list of places i've been offset from my start at 0,0. This is a 2D cartesian plane.
I've been given 2 properties, my translation speed from 0-1 and my rotation speed from -1 to 1. -1 is very left and +1 is very right. These are speed and not angles so thats where my problem lies. If I'm given 0 as a translation speed and 0.2 I will continually turn to my right at a slow speed.
How do I figure out the offsets given these 2 variables? I can store it every time I take a 'step'.
I just need to figure out the offsets in x and y terms given the translations and rotation speeds. And the rotation to get to those points.
Any help is appreciated.
Your question is unclear on a couple of points, so I have to make some assumptions:
During each time interval, translation speed and rotational velocity are constant.
You know the values of these variables in every time interval (and you know rotational velocity in usable units, like radians per second, not just "very left").
You know initial heading.
You can maintain enough precision that roundoff error is not a problem.
Given that, there is an exact solution. First the easy part:
delta_angle = omega * delta_t
Where omega is the angular velocity. The distance traveled (maybe along a curve) is
dist = speed * delta_t
and the radius of the curve is
radius = dist / delta_angle
(This gets huge when angular velocity is near zero-- we'll deal with that in a moment.) If angle (at the beginning of the interval) is zero, defined as pointing in the +x direction, then the translation in the interval is easy, and we'll call it deta_x_0 and delta_y_0:
delta_x_0 = radius * sin(delta_angle)
delta_y_0 = radius * (1 - cos(delta_angle))
Since we want to be able to deal with very small delta_angle and very large radius, we'll expand sin and cos, and use this only when angular velocity is close to zero:
dx0 = r * sin(da) = (dist/da) * [ da - da^3/3! + da^5/5! - ...]
= dist * [ 1 - da^2/3! + da^4/5! - ...]
dy0 = r * (1-cos(da)) = (dist/da) * [ da^2/2! - da^4/4! + da^6/6! - ...]
= dist * [ da/2! - da^3/4! + da^5/6! - ...]
But angle generally isn't equal to zero, so we have to rotate these displacements:
dx = cos(angle) * dx0 - sin(angle) * dy0
dy = sin(angle) * dx0 - cos(angle) * dy0
You could do it in two stages. First work out the change of direction to get a new direction vector and then secondly work out the new position using this new direction. Something like
angle = angle + omega * delta_t;
const double d_x = cos( angle );
const double d_y = sin( angle );
x = x + d_x * delta_t * v;
y = y + d_y * delta_t * v;
where you store your current angle out at each step. ( d_x, d_y ) is the current direction vector and omega is the rotation speed that you have. delta_t is obviously your timestep and v is your speed.
This may be too naive to split it up into two distinct stages. I'm not sure I haven't really thought it through too much and haven't tested it but if it works let me know!

Perturb vector by some angle

I have a unit vector in 3D space whose direction I wish to perturb by some angle within the range 0 to theta, with the position of the vector remaining the same. What is a way I can accomplish this?
Thanks.
EDIT: After thinking about the way I posed the question, it seems to be a bit too general. I'll attempt to make it more specific: Assume that the vector originates from the surface of an object (i.e. sphere, circle, box, line, cylinder, cone). If there are different methods to finding the new direction for each of those objects, then providing help for the sphere one is fine.
EDIT 2: I was going to type this in a comment but it was too much.
So I have orig_vector, which I wish to perturb in some direction between 0 and theta. The theta can be thought of as forming a cone around my vector (with theta being the angle between the center and one side of the cone) and I wish to generate a new vector within that cone. I can generate a point lying on the plane that is tangent to my vector and thus creating a unit vector in the direction of the point, call it rand_vector. At this time, I orig_vector and trand_vector are two unit vectors perpendicular to each other.
I generate my first angle, angle1 between 0 and 2pi and I rotate rand_vector around orig_vector by angle1, forming rand_vector2. I looked up a resource online and it said that the second angle, angle2 should be between 0 and sin(theta) (where theta is the original "cone" angle). Then I rotate rand_vector2 by acos(angle2) around the vector defined by the cross product between rand_vector2 and orig_vector.
When I do this, I don't obtain the desired results. That is, when theta=0, I still get perturbed vectors, and I expect to get orig_vector. If anyone can explain the reason for the angles and why they are the way they are, I would greatly appreciate it.
EDIT 3: This is the final edit, I promise =). So I fixed my bug and everything that I described above works (it was an implementation bug, not a theory bug). However, my question about the angles (i.e. why is angle2 = sin(theta)*rand() and why is perturbed_vector = rand_vector2.Rotate(rand_vector2.Cross(orig_vector), acos(angle2)). Thanks so much!
Here's the algorithm that I've used for this kind of problem before. It was described in Ray Tracing News.
1) Make a third vector perpendicular to the other two to build an orthogonal basis:
cross_vector = unit( cross( orig_vector, rand_vector ) )
2) Pick two uniform random numbers in [0,1]:
s = rand( 0, 1 )
r = rand( 0, 1 )
3) Let h be the cosine of the cone's angle:
h = cos( theta )
4) Modify uniform sampling on a sphere to pick a random vector in the cone around +Z:
phi = 2 * pi * s
z = h + ( 1 - h ) * r
sinT = sqrt( 1 - z * z )
x = cos( phi ) * sinT
y = sin( phi ) * sinT
5) Change of basis to reorient it around the original angle:
perturbed = rand_vector * x + cross_vector * y + orig_vector * z
If you have another vector to represent an axis of rotation, there are libraries that will take the axis and the angle and give you a rotation matrix, which can then be multiplied by your starting vector to get the result you want.
However, the axis of rotation should be at right angles to your starting vector, to get the amount of rotation you expect. If the axis of rotation does not lie in the plane perpendicular to your vector, the result will be somewhat different than theta.
That being said, if you already have a vector at right angles to the one you want to perturb, and you're not picky about the direction of the perturbation, you can just as easily take a linear combination of your starting vector with the perpendicular one, adjust for magnitude as needed.
I.e., if P and Q are vectors having identical magnitude, and are perpendicular, and you want to rotate P in the direction of Q, then the vector R given by R = [Pcos(theta)+Qsin(theta)] will satisfy the constraints you've given. If P and Q have differing magnitude, then there will be some scaling involved.
You may be interested in 3D-coordinate transformations to change your vector angle.
I don't know how many directions you want to change your angle in, but transforming your Cartesian coordinates to spherical coordinates should allow you to make your angle change as you like.
Actually, it is very easy to do that. All you have to do is multiply your vector by the correct rotation matrix. The resulting vector will be your rotated vector. Now, how do you obtain such rotation matrix? That depends on the 3d framework/engine you are using. Any 3d framework must provide functions for obtaining rotation matrices, normally as static methods of the Matrix class.
Good luck.
Like said in other comments you can rotate your vector using a rotation matrix.
The rotation matrix has two angles you rotate your vector around. You can pick them with a random number generator, but just picking two from a flat generator is not correct. To ensure that your rotation vector is generated flat, you have to pick one random angle φ from a flat generator and the other one from a generator flat in cosθ ;this ensures that your solid angle element dcos(θ)dφ is defined correctly (φ and θ defined as usual for spherical coordinates).
Example: picking a random direction with no restriction on range, random() generates flat in [0,1]
angle1 = acos(random())
angle2 = 2*pi*random()
My code in unity - tested and working:
/*
* this is used to perturb given vector 'direction' by changing it by angle not more than 'angle' vector from
* base direction. Used to provide errors for player playing algorithms
*
*/
Vector3 perturbDirection( Vector3 direction, float angle ) {
// division by zero protection
if( Mathf.Approximately( direction.z, 0f )) {
direction.z = 0.0001f;
}
// 1 get some orthogonal vector to direction ( solve direction and orthogonal dot product = 0, assume x = 1, y = 1, then z = as below ))
Vector3 orthogonal = new Vector3( 1f, 1f, - ( direction.x + direction.y ) / direction.z );
// 2 get random vector from circle on flat orthogonal to direction vector. get full range to assume all cone space randomization (-180, 180 )
float orthoAngle = UnityEngine.Random.Range( -180f, 180f );
Quaternion rotateTowardsDirection = Quaternion.AngleAxis( orthoAngle, direction );
Vector3 randomOrtho = rotateTowardsDirection * orthogonal;
// 3 rotate direction towards random orthogonal vector by vector from our available range
float perturbAngle = UnityEngine.Random.Range( 0f, angle ); // range from (0, angle), full cone cover guarantees previous (-180,180) range
Quaternion rotateDirection = Quaternion.AngleAxis( perturbAngle, randomOrtho );
Vector3 perturbedDirection = rotateDirection * direction;
return perturbedDirection;
}

Resources