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
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.
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.
I am trying to code for a game I am working on a specific curve with a specific rotation. I am not a great mathematician... At all... Tried searching for solutions for a few hours, but I'm affraid I do not find any solution.
So, a small picture to illustrate first:
This is an eighth of a circle, radius of 9, beggining is (0,0)
The end is now at about 6.364, -2.636. But I need this same curve, with a 45° direction at the end, but ending at aexactly 6.0,-3.0.
Could any of you show me how to do this? I need to be able to calculate precisly any point on this curve & its exact length. I would suppose using some kind of eliptical math could be a solution? I admit my math class are reaaaly far now and have now good clue for now...
Thank for any possible help
I think I found a quadratic curve which sastisfies your requirement:
f(x) = -1/12 x^2 + 9
Copy the following into https://www.desmos.com/calculator to see it:
-\frac{1}{12}x^2+9
f'(x) would be -1/6x, so when x=6, the derivative would be -1, which corresponds to a -45° inclination. There are probably infinite curves that satisfy your requirement but if my calculus isn't too rusty this is one of them.
I tried to fit an ellipse with foci starting at y=6 here and starting at y=9 here to your points but the slope doesn't look like 45°.
Also starting at any height k, here doesn't seem to work.
I don't think you've fully understood the question I asked in the comments about the "inclination" angle. So I will give a general case solution, where you have an explicit tangent vector for the end of the curve. (You can calculate this using the inclination angle; if we clarify what you mean by it then I will be happy to edit with a formula to calculate the tangent vector if necessary)
Let's draw a diagram of how the setup can look:
(Not 100% accurate)
A and B are your fixed points. T is the unit tangent vector. r and C are the radius and center of the arc we need to calculate.
The angle θ is given by the angle between BA and T minus π/2 radians (90 degrees). We can calculate it using the dot product:
The (signed) distance from the center of AB to C is given by:
Note that this is negative for the case on the right, and positive for the left. The radius is given by:
(You can simplify by substituting and using a cosine addition rule, but I prefer to keep things in terms of variables in the diagram). To obtain the point C, we need the perpendicular vector to AB (call it n):
Now that we have the radius and center of the circular arc, we still need to determine which direction we are moving in, i.e. whether we are moving clockwise or anti-clockwise when going from A to B. This is a simple test, using the cross-product:
If this is negative, then T is as in the diagram, and we need to move clockwise, and vice versa. The length of the arc l, and the angular displacement γ when we move by a distance x along the arc:
Nearly there! Just one more step - we need to work out how to rotate the point A by angle γ around point C, to get the point we want (call it D):
(Adapted from this Wikipedia page)
Now for some code, in case the above was confusing (it probably was!):
public Vector2 getPointOnArc(Vector2 A, Vector2 B, Vector2 T, double x)
{
// calculate preliminaries
Vector2 BA = B - A;
double d = BA.Length();
double theta = Math.Acos(Vector2.DotProduct(BA, T) / d) - Math.PI * 0.5;
// calculate radius
double r = d / (2.0 * Math.Cos(theta));
// calculate center
Vector2 n = new Vector2(BA.y, -BA.x);
Vector2 C = 0.5 * (A + B + n * Math.Tan(theta));
// calculate displacement angle from point A
double l = (Math.PI - 2.0 * theta) * r;
double gamma = (2.0 * Math.PI * x) / l;
// sign change as discussed
double cross = T.x * BA.y - T.y * BA.x;
if (cross < 0.0) gamma = -gamma;
// finally return the point we want
Vector2 disp = A - C;
double c_g = Math.Cos(gamma), s_g = Math.Sin(gamma);
return new Vector2(disp.X * c_g + disp.Y * s_g + C.X,
disp.Y * c_g - disp.X * s_g + C.Y);
}
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.
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);