Three.js reverse rotation - math

I'm placing Object3Ds in a scene with coordinates from an older Away3D project. The issue I'm having is when objects have a negative position.x (i.e. behind the camera), the rotation is opposite.
I'm only using X and Z for tilt and spin.
Is there a way to calculate the opposite/flip side angles so it flips?
i.e. if x = 1.69, what should the direct opposite be?
Here is my current attempt:
photo.rotation.x = data.rotationX != null
? photo.position.x < 0
? Math.PI - data.rotationX
: data.rotationX
: 0;
photo.position.x < 0
? photo.rotation.z = Math.PI
: photo.rotation.z = 0;
photo.rotation.y = data.rotationY != null
? photo.position.x < 0
? Math.PI - data.rotationY
: data.rotationY
: 0;
EDIT: Doing some further research on this, is it possible the Three.js way of calculating angles is going to be different from the Away3d one? i.e. a similar problem here 3D rotation with Axis & Angle
It's very strange. Some of the objects are good and the same as they are in Away3d, and some are on strange angles that are not anywhere near the same.

Related

How to determine if need to swap start and end angle when drawing an arc

I have multiple polygon maps (made up of lines and arcs). I am using turf.lineArc() to calculate points on an arc and to do this the start and end points of the arc need to be clockwise, if not they need to be swapped around.
I have the following code to swap the start and end points around (but it is not quite right)
if (endAngle < startAngle) {
endAngle = endAngle + 360;
}
if (startAngle < endAngle) {
var e = endAngle;
endAngle = startAngle;
startAngle = e;
}
while (startAngle - endAngle > 180) {
startAngle = startAngle - 360;
}
var arc = turf.lineArc(center, radius, startAngle, endAngle, options);
My problem is knowing when to swap the start and end around and when not to. In my attached picture Map1 works correctly without being swapped around but Map2 needs to have the start and end points swapped. (and they both need to use the same code). If map 2 does not have the start and end swapped around turf.lineArc draws a major arc of 353 degrees which is not what I want.
How do I fix my code so I only swap the start and end points when travelling from start to end is in an anti-clockwise direction?
Thank you :)
Edit: Arc can be < 180 or >180 and I know if it is major (>180) or minor (<180)
If your desired arc always should be < 180 degrees, then you can apply the next approach to overcome periodicity and zero-crossing pitfalls:
if Math.sin(endAngle-startAngle) < 0 then swap
I think, angles should be in radians in turfjs.
Also check - perhaps you have to change <0 to >0 to provide clockwise direction in your coordinate system.
I used this by Corrl to determine clockwise direction so then knew if to swap or not.
JavaScript - Find turn direction from two angles

When rotating 2D sprite towards cursor via angularVelocity, it spins at one point

Intro
I've created a spaceship sprite in my Unity project, I wanted it to rotate towards the cursor via angular velocity, because I'd like make my game to be heavily physics based.
Problem
Now my problem with rotating the sprite via by angular velocity is the following:
At -180° / 180° rotation my ship spins around, because while my mouse's angle is already 180°, while my ship's rotation is still -180°, or the other way around.
I tried
I tried to solve it mathematically, wasn't too successful, I could make it spin the right way just much slower/faster, I could fix the 180/-180 point, but made two different ones instead.
Looked for different solutions, but couldn't find a more fitting one.
Code
So I have this code for the rotation:
// Use this for initialization
void Start () {
rb = gameObject.GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update () {
//getting mouse position in world units
mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
//getting the angle of the ship -> cursor vector
angle = Mathf.Atan2(mousePos.y - transform.position.y, mousePos.x - transform.position.x) * Mathf.Rad2Deg;
//getting the angle between the ship -> cursor and the rigidbody.rotation vector
diffAngle = angle - (rb.rotation + 90);
//Increasing angular velocity scaling with the diffAngle
rb.angularVelocity = diffAngle * Time.deltaTime * PlayerShipStats.Instance.speed * 100f;
Thank you for your contribution in advance
Solution for Problem 1
Inserting this code made it work, not for long :
if(diffAngle > 180) {
diffAngle -= 360;
} else if (diffAngle < -180) {
diffAngle += 360;
}
Problem 2 and Solution for Problem 2
The new problem is:
rigidbody.rotation can exceed it's boundaries, it can be rotated for more than 360 degrees.
this code patched this bug:
if(rb.rotation + 90 >= 180) {
rb.rotation = -270;
} else if (rb.rotation + 90 <= -180) {
rb.rotation = 90;
}
The perfect code
void AimAtTarget(Vector2 target, float aimSpeed) {
//getting the angle of the this -> target vector
float targetAngle = Mathf.Atan2(target.y - transform.position.y, target.x - transform.position.x) * Mathf.Rad2Deg;
if (rb.rotation + 90 >= 180) {
rb.rotation = -270;
} else if (rb.rotation + 90 <= -180) {
rb.rotation = 90;
}
//getting the angle between the this -> target and the rigidbody.rotation vector
float diffAngle = targetAngle - (rb.rotation - 90);
if (diffAngle > 180) {
diffAngle -= 360;
} else if (diffAngle < -180) {
diffAngle += 360;
}
//Increasing angular velocity scaling with the diffAngle
rb.angularVelocity = diffAngle * Time.deltaTime * aimSpeed * 100;
}
There are two problems I see here:
Problem 1
angle is always going to be between -180 and 180, while rb.rotation is between 0 and 360. So you are comparing angles using two different notations. The first step is to get both angles returning -180 to 180 or 0 to 360. I chose to do the following which puts both angles between -180 and 180:
//getting the angle of the ship -> cursor vector
float targetAngle = Mathf.Atan2(
mousePos.y - transform.position.y,
mousePos.x - transform.position.x) * Mathf.Rad2Deg;
//get the current angle of the ship
float sourceAngle = Mathf.Atan2(
this.transform.up.y,
this.transform.up.x) * Mathf.Rad2Deg;
Problem 2
If you fix problem 1 and tried your app you would notice that the ship sometimes rotates the wrong way, although it will eventually get to its target. The problem is that diffAngle can sometimes give a result that is greater than +180 degrees (or less than -180). When this happens we actually want the ship to rotate the other direction. That code looks like this:
//getting the angle between the ship -> cursor and the rigidbody.rotation vector
float diffAngle = targetAngle - sourceAngle;
//use the smaller of the two angles to ensure we always turn the correct way
if (Mathf.Abs(diffAngle) > 180f)
{
diffAngle = sourceAngle - targetAngle;
}
I made a simple Unity to verify this works. I was able to rotate my ship in either direction smoothly.
One thing you may have to handle, if you don't already, is appropriately stopping the rotation of the ship when the it is facing the cursor. In my test I noticed that the ship would jitter slightly when it reached its target because it would (very) slightly overshoot the cursor's angle in one direction and then the other. The larger the value of PlayerShipStats.Instance.speed the more pronounced this effect will likely be.

Rotation matrix by yaw

I want to set the yaw of a rotation matrix so an object points to a specific position using this code
Vector3 dist = transform().position() - mPlayerTarget;
transform().rotationZ(atan2(dist.x(), dist.y()));
This would produce the right results, except the rotation is inverse, so instead of following the target point it rotates away from it.
Vector3 dist = transform().position() - mPlayerTarget;
transform().rotationZ(-atan2(dist.x(), dist.y()));
(with -atan2) the object follows the target, but it's offset by a 90 degrees to the right. The rotationZ implementation looks like this:
float cz = cosf(rotation);
float sz = sinf(rotation);
matrix.mMatrix[0] = cz;
matrix.mMatrix[1] = sz;
matrix.mMatrix[2] = 0;
matrix.mMatrix[3] = -sz;
matrix.mMatrix[4] = cz;
matrix.mMatrix[5] = 0;
matrix.mMatrix[6] = 0;
matrix.mMatrix[7] = 0;
matrix.mMatrix[8] = 1;
I'm using iOS OpenGL ES 2.0. Something seems fundamentally wrong here, the first version should be the one producing the right results? All the other transformations seem to work properly. What could go wrong here? I don't know where to look for errors...
First thing is atan2 - it is usually defined as atan2(y, x), whereas you have it the other way around.
Another source of issues might be the direction of your dist vector - it goes from the target towards the transform position. Try reversing it.

Calculating if or not a 3D eyepoint is behind a 2D plane or upwards

The setup
Draw XY-coordinate axes on a piece of paper. Write a word on it along X-axis, so that the word's centerpoint is at origo (half on positive side of X/Y, the other half on negative side of X/Y).
Now, if you flip the paper upside down you'll notice that the word is mirrored in relation to both X- and Y-axis. If you look from behind the paper, it's mirrored in relation to Y-axis. If you look at it from behind and upside down, it's mirrored in relation to X-axis.
Ok, I have points in 2D-plane (vertices) that are created in similar way at the origo and I need to apply exactly the same rule for them. To make things interesting:
The 2D plane is actually 3D, each point (vertex) being (x, y, 0). Initially the vertices are positioned to the origo and their normal is Pn(0,0,1). => Correctly seen when looked at from point Pn towards origo.
The vertex-plane has it's own rotation matrix [Rp] and position P(x,y,z) in the 3D-world. The rotation is applied before positioning.
The 3D world is "right handed". The viewer would be looking towards origo from some distance along positive Z-axis but the world is also oriented by rotation matrix [Rw]. [Rw] * (0,0,1) would point directly to the viewer's eye.
From those I need to calculate when the vertex-plane should be mirrored and by which axis. The mirroring itself can be done before applying [Rp] and P by:
Vertices vertices = Get2DPlanePoints();
int MirrorX = 1; // -1 to mirror, 1 NOT to mirror
int MirrorY = 1; // -1 to mirror, 1 NOT to mirror
Matrix WorldRotation = GetWorldRotationMatrix();
MirrorX = GetMirrorXFactor(WorldRotation);
MirrorY = GetMirrorYFactor(WorldRotation);
foreach(Vertex v in vertices)
{
v.X = v.X * MirrorX * MirrorY;
v.Y = V.Y * MirrorY;
}
// Apply rotation...
// Add position...
The question
So I need GetMirrorXFactor() & ..YFactor() -functions that return -1 if the viewer's eyepoint is at greater "X/Y"-angle than +-90 degrees in relation to the vertex-plane's normal after the rotation and world orientation. I have already solved this, but I'm looking for more "elegant" mathematics. I know that rotation matrices somehow contain info about how much is rotated by which axis and I believe that can be utilized here.
My Solution for MirrorX:
// Matrix multiplications. Vectors are vertical matrices here.
Pnr = [Rp] * Pn // Rotated vertices's normal
Pur = [Rp] * (0,1,0) // Rotated vertices's "up-vector"
Wnr = [Rw] * (0,0,1) // Rotated eye-vector with world's orientation
// = vector pointing directly at the viewer's eye
// Use rotated up-vector as a normal some new plane and project viewer's
// eye on it. dot = dot product between vectors.
Wnrx = Wnr - (Wnr dot Pur) * Pur // "X-projected" eye.
// Calculate angle between eye's X-component and plane's rotated normal.
// ||V|| = V's norm.
angle = arccos( (Wnrx dot Pnr) / ( ||Wnrx|| * ||Pnr|| ) )
if (angle > PI / 2)
MirrorX = -1; // DO mirror
else
MirrorX = 1; // DON'T mirror
Solution for mirrorY can be done in similar way using viewer's up and vertex-plane's right -vectors.
Better solution?
if (([Rp]*(1,0,0)) dot ([Rw]*(1,0,0))) < 0
MirrorX = -1; // DO mirror
else
MirrorX = 1; // DON'T mirror
if (([Rp]*(0,1,0)) dot ([Rw]*(0,1,0))) < 0
MirrorY = -1; // DO mirror
else
MirrorY = 1; // DON'T mirror
Explaining in more detail is difficult without diagrams, but if you have trouble with this solution we can work through some cases.

Detect when a 2D moving object crosses its own path?

How can I detect when a 2D moving object has crossed its own path?
I store the path as an array of points based on the plane's previous positions.
Pseudo-code or any programming language can be used to describe a solution.
Here's my code I've tried already - it detects a full 360 loop. I think I need a different approach.
CGFloat angleDiff = angleCurr - lastAngleRecorded;
lastAngleRecorded = angleCurr;
// Ensure -180 < angleDiff < 180
angleDiff = angleDiff > M_PI ? angleDiff - (M_PI*2) : angleDiff;
angleDiff = angleDiff < -M_PI ? angleDiff + (M_PI*2) : angleDiff;
// Reset tracking of the loop of the plane's angle exceeds (turns too sharply) or falls below the limits
if(fabsf(angleDiff) < angleDiffMinAllowed || fabsf(angleDiff) > angleDiffMaxAllowed) {
if(++ringFaultCount >= ringFaultCountMax) {
[self resetTracking];
return;
}
}
ringFaultCount = 0;
// Add plane position to ring polygon
[ringPoints addObject:[NSValue valueWithCGPoint: ccp(targetPlane.position.x, targetPlane.position.y)]];
// Add angleDiff to angleTotal
angleTotal += angleDiff;
// Completed loop?
if(fabsf(angleTotal) >= M_PI * 2.f) {
[self resetTracking];
if(isRingJoined){
CCLOG(#"%# RING COMPLETE", NSStringFromSelector(_cmd));
}
return;
}
I also had the problem, I solved it by making a straight line in a coordinate system:
y = mx+q±­tolerance
Let me explain:
The line is the tangent of the curve at the point you check if there is a collision this is the line the "aircraft" followed in that point.
The tolerance will make the line move a litlle bit up and also one a little bit down.
so you get 2 parralel lines witch can be seen as boundarys.
you also have to make a tolerance on the x-axis
The m is the direction of the line, its: tan(angle), the angle is the angle with the x-axis.
If all that is setup than you have to do this:
if(y_point < mx+q+tolerance && y_point> mx+q-tolerance && x_points > x-tolerance && x_point< x+tolerance
{
// some code
}

Resources