We are using Unity C#. This image is a simplified 2D situation, in which we know the coordinates (x,y) of points p1 and p2.
We know the angle Theta, using this beauty
static float CalculateAngle(Vector3 p1, Vector3 p2)
{ return Quaternion.FromToRotation(Vector3.right, p1 - p2).eulerAngles.z; }
// The use of Vector3.right make zero degrees start at 3h00
// The values of z are irrelevant in this post, always zeroed.
Now a new point p3 shows up, imagine a screen touch, so we know it's coordinates (x,y). In essence, everything blue in the image, we know it's values.
The question is this: how to calculate a new p4 coordinates, in which
we know p3(x,y) coordinates
we don't know p4(x,y), except that:
p4.y has to be equal to p3.y
p4 is in line with p1 and p2
How to calculate the unknown p4.x, to have the full p4(x,y) coordinates, using Unity C#?
There may be easier solutions out there. The basic Math solution is as follows:
Calculate the linear function of p1 and p2 as mentioned here. An equation in the slope-intercept form is written as
y = mx + b
where m is the slope of the line and b is the y-intercept.
Insert P3's y into the form.
Solve x.
Example in C# for Unity:
Vector3 p1 = new Vector3(1f, 2f);
Vector3 p2 = new Vector3(2f, 3f);
Vector3 p3 = new Vector3(1000f, 5f);
Vector3 p4 = Vector3.zero;
float m = ((p2.y - p1.y) / (p2.x - p1.x));
float b = p1.y - (m * p1.x);
// x = ( y - b ) / m
p4.x = (p3.y - b) / m;
p4.y = p3.y;
print(p4); // (4.0, 5.0, 0.0) as expected
Linear functions are of the form y = mx + b, where m is the slope and b is the vertical shift. If P3 is a point with an xy values, then you can take the slope, the offset, and y and solve for x:
(y - b) / m = x
This is more of a general math question than a unity specific question; in the future, I'd suggest trying the stack exchange math site.
This will solve your problem, and it also works for p1,p2, and/or p3 with different depths:
Create a plane where Y=p3.y, and then raycast from p1->p2 to find where it intersects.
Plane plane = new Plane(Vector3.up, p3);
float enter;
Ray ray = new Ray(p1, p2-p1);
bool doesIntersect = plane.Raycast(ray, out enter);
if (doesIntersect) {
Vector3 p4 = ray.GetPoint(enter);
DoStuff(p4.x);
}
If you need to project along a non-horizontal direction, you'll need to use a different normal than Vector3.up.
Related
I have a triangle defined by 3 points (System.Numerics.Vector3, I'm not using Unity3d):
A [-0,21090554, 3,81115985, -23,54532623]
B [0,01877949, 3,79133320, 23,49146652]
C [33,08344650, 1,99846101, 0,20934258].
As you can see triangle is slightly tilted and is not parallel to the ground:
I need to rotate the triangle so it will become parallel to the ground (all three points Y coordinates will become the same).
So I need to calulate a rotation to align triangle normal with world up vector [0, 1, 0] (vertical normal on my picture is [0, 10, 0] because [0, 1, 0] vector will be too short to see and to distinguish it from the triangle normal).
I'm new to 3D and have no idea howto calculate a rotation (Quaternion or Matrix I guess) and apply it to all triangle points.
Help me with a code sample, please.
Thank you.
You can use Quaternion.CreateFromAxisAngle to create the quaternion.
var normal = Vector3 Normalize((A - B) * (C - B));
var toDir = Vector3.UnitY;
var axis = normal * toDir;
var angle = Math.Acos(Vector3.Dot(normal, toDir));
var rot = Quaternion.CreateFromAxisAngle(axis, angle);
To rotate the 3 points, you need define an origin first, then use Vector3.Transform transform the points by the quaternion.
var O = (A + B + C) / 3;
var Ar = Vector3.Transform(A - O, rot) + O;
var Br = Vector3.Transform(B - O, rot) + O;
var Cr = Vector3.Transform(C - O, rot) + O;
Consider the Cross Product between the vertical and triangle normal.
Direction of the Cross Product becomes direction of your rotation axis.
From definition of Cross Product, you can obtain the info(sin value) about the rotation angle.
Now, you have axis and angle.
So, you can represent the rotation as Quaternion or some other representation.
There are the planet and a few satellites in 3d space. I need to calculate axis of rotation for every satellite. They should rotate around planet center.
I calculated the vector from satellite to the center of the planet.
vec1 = planetCenter - sputnikCenter;
With vec1 and planetCenter I can calculate the equation of plane, that perpendicular to vec1.
equation like that:
A.x + B.y + C.z + D = 0
Now, I should get random vector on this plane. This vector will be axis of rotation. But how can I get this random vector?
well if you got the plane A.x + B.y + C.z + D = 0 then n(A,B,C) is the normal vector. So I think the easiest approach to your task is to use basis vectors. So you need 2 perpendicular vectors on this plane. For that you can exploit cross product. first some definitions:
knowns:
p planet center position (or the center point of your rotations or any point on the plane so in worst case you can try p=0,0,-D/C or any other combinationn...)
n normal vector
q= (1,0,0) or (0,1,0) chose the one that has lesser |dot(n,q)|
operations:
vector = cross(a,b) = a x b - cross product returns perpendicular vector to a,b
scalar = dot(a,b) = (a.b) - dot product returns 0 if a,b are perpendicular
|a| = abs(a) - absolute value (both scalar and vector)
scalar = Rand() - float pseudo random value on interval <0.0,1.0>
unknowns:
u,v - basis vectors
r - your pseudo-random point
So first get u,v by exploiting cross product:
u=cross(n,q)
v=cross(n,u)
And now the point:
r = p + u*(2.0*Rand()-1.0) + v*(2.0*Rand()-1.0)
If you want just random vector then ignore the start position p
r' = u*(2.0*Rand()-1.0) + v*(2.0*Rand()-1.0)
That is all ... so you can compute u,v once (per normal vector change) and generate the r as often as you need. If u,v are unit vectors then this will generate points inside 2x2 square ... if you want more or less just add scales to them ...
see Is it possible to make realistic n-body solar system simulation? and generate random orbital parameters for Kepler's equation instead ...
It seems your rotation axis might be random vector that is independent from vec1.
You can generate random unit vector with uniform distribution using methods for Sphere Point Picking.
Marsaglia method (eq. 9-11) is convenient to generate this vector:
Generate x1 and x2 in range -1..1 such as p = x1^2 +x2^2 <= 1 (rejecting bad pairs).
Then
x = 2 * x1 * Sqrt(1 - p)
y = 2 * x2 * Sqrt(1 - p)
z = 1 - 2 * p
Now your question is clear. You want to rotate object around another, like Earth and Sun. May be some other solutions may available but I would do it through LookAt and parametric equation of circle.
x = r * cos(theta) + displacementX
z = r * sin(theta) + displacementZ
where r is radius, distance in your case
displacementX and displacementZ are the distance from origin. If both (displacementX and displacementZ) is 0 then it will rotate around origin (0,0)
In Object(Earth) script, do it as follow
public Transform _sun;
float _theta = 0;
void Start ()
{
StartCoroutine ("ChangeAngle");
}
void Update ()
{
transform.LookAt (_sun);
float newX = (5 * Mathf.Cos (_theta)) + _sun.position.x;
float newZ = (5 * Mathf.Sin (_theta)) + _sun.position.z;
transform.position = new Vector3 (newX, _sun.position.y, newZ);
}
IEnumerator ChangeAngle ()
{
while (true) {
yield return new WaitForSeconds (0.01f);
_theta += 0.1f;
if (_theta >= 360)
_theta = 0;
}
}
You can further play with it
For a screen overlay I am making for a 3-dimensional game, I need to display icons over certain locations in the 3D world. I've managed to get several variables from the game that should make this possible:
Player position (x,y,z)
Camera position (x,y,z)
Point position (x,y,z)
Camera Angle(FOV)
Using these variables I managed to get the right and left edge of the camera view. Using these 2 variables I am able to get the point between 0 and 1 where the icon should be displayed on the x axis. (0 is on the left edge, 1 is right edge) This seems to work quite well, but only if I am aligned to either the X or Z axis, as shown in the following example:
I've been trying to fix this using the following rotation matrix:
[ Math.Cos(angle), -Math.Sin(angle) ]
[ Math.Sin(angle), Math.Cos(angle) ]
What I do is, I put the player position, camera position and the camera edge positions in the matrix with as rotation point the world point. The problem is, as soon as I put the angle amount at 90 degrees, the X and Y are being flipped. I've been trying to find a solution for this for a few days now, but I can't find it, so I hope anyone can push me in the right direction here. Below are a few parts of my code that might help in finding the solution:
float PCDistXZ = (float)Math.Sqrt(deltaPCx * deltaPCx + deltaPCz * deltaPCz); // X/Z distance from the world point to the camera
Point fakeAvatarPos = RotateAround(new Point((int)point.x, (int)point.z), new Point((int)avatar.x, (int)avatar.z), (int)90);
Point fakeCameraPos = RotateAround(new Point((int)point.x, (int)point.z), new Point((int)camera.x, (int)camera.z), (int)90);
double edgeRight = fakeC.X + (Math.Sin(45) * PCDistXZ);
double edgeLeft = fakeC.X - (Math.Sin(45) * PCDistXZ);
float edgeTest_ScreenPositionX = (1 - (float)((edgeRight - P.x) / (edgeRight - edgeLeft))) * screenWidth;
public static Point RotateAround(Point pCenter,Point pPoint, float pAngle)
{
double angle = (pAngle * Math.PI) / 180;
double[,] matrix = new Double[2, 2] {
{ Math.Cos(angle), Math.Sin(angle) },
{ Math.Sin(angle), Math.Cos(angle) }
};
double xOffset = pPoint.X - pCenter.X;
double yOffset = pPoint.Y - pCenter.Y;
Point newPoint = new Point(
(int)(((pPoint.X - xOffset) * matrix[0, 0]) - ((pPoint.Y - xOffset) * matrix[0, 1])),
(int)(((pPoint.X - yOffset) * matrix[1, 0]) + ((pPoint.Y - yOffset) * matrix[1, 1]))
);
newPoint.X += (int)xOffset;
newPoint.Y += (int)yOffset;
return new Point(newPoint.X,newPoint.Y);
}
Note: I've changed the names of some of the variables to more understandable one, so it could be possible that there are inconsistencies in the names.
EDIT: I found out about view- and projection matrices. I might be able to use those to convert the 3D position to screen. I'm not sure if it's possible to make this matrices with the limited information I have though.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to calculate the angle between two points relative to the horizontal axis?
I've been looking for this for ages and it's just really annoying me so I've decided to just ask...
Provided I have two points (namely x1, y1, and x2, y2), I would like to calculate the angle between these two points, presuming that when y1 == y2 and x1 > x2 the angle is 180 degrees...
I have the below code that I have been working with (using knowledge from high school) and I just can't seem to produce the desired result.
float xDiff = x1 - x2;
float yDiff = y1 - y2;
return (float)Math.Atan2(yDiff, xDiff) * (float)(180 / Math.PI);
Thanks in advance, I'm getting so frustrated...
From what I've gathered, you want the following to hold:
Horizontal line: P1 -------- P2 => 0°
Horizontal line: P2 -------- P1 => 180°
Rotating the horizontal line clockwise
You said, you want the angle to increase in clockwise direction.
Rotating this line P1 -------- P2 such that P1 is above P2, the angle must thus be 90°.
If, however, we rotated in the opposite direction, P1 would be below P2 and the angle is -90° or 270°.
Working with atan2
Basis: Considering P1 to be the origin and measuring the angle of P2 relative to the origin, then P1 -------- P2 will correctly yield 0.
float xDiff = x2 - x1;
float yDiff = y2 - y1;
return Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI;
However, atan2 let's the angle increase in CCW direction.
Rotating in CCW direction around the origin, y goes through the following values:
y = 0
y > 0
y = 0
y < 0
y = 0
This means, that we can simply invert the sign of y to flip the direction. But because C#'s coordinates increase from top to bottom, the sign is already reversed when computing yDiff.
I got a Plane (Normal, d) and a Vector3 point (x, y, z).
I need to translate the plane to that point for X distance. How do I do that?
I'm coming up with this..
plane = Plane.Transform(plane, Matrix.CreateTranslation(
But can't figure what to place there. It has to be something with dot product, Plane.Normal and my Vector3.
EDIT:
I'm thinking of this.
public static Plane MoveTo(this Plane p, Vector3 point, float distance)
{
Vector3 planeVector = p.Normal * p.D;
Matrix matrix = Matrix.CreateTranslation(Vector3.Normalize(planeVector)) *
distance * Math.Sign(Vector3.Dot(planeVector, point - planeVector))
return Plane.Transform(p, matrix);
}
If someone considers this as wrong or particually wrong, please, note it.
The distance from a point P to the plane Pi is:
You should calc current d(P, pi), substract to that the amount X, and then only have to calculate D to get the new plane.
EDIT:
// This line has no sense... is useless do that.
Vector3 planeVector = p.Normal * p.D;
To know the relation between a point and a plane, you only have to calculate its equation: R = Ax + By + Cz + D where (A,B,C) is the normal and (x,y,z) the point...
if (R == 0) the point is contained in the plane
if (R>0) the point is front // or viceversa
if (R<0) that point is back
R = plane.DotCoordinate(point);
distance*=(R>0) ? 1 : -1; // or viceversa, i'm not sure now
Matrix matrix = Matrix.CreateTranslation(plane.Normal * distance);
return Plane.Transform(p, matrix);