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);
Related
Working in Unity
I have a working solution using raycasts and a sphere collider, but would like to understand how to accomplish the same result using maths alone.
Scenario is as such:
a) I have a thoretical sphere
b) I project a line from any point within the sphere along any path/direction (both of which are expressed as a Vector3s)
c) I would like to determine the point (also a Vector3) at which the path intersects with the surface of the sphere, as if detected by a raycast returning point data.
I am familiar with the use of COS and SIN plotting points on a 2D plane, but not in 3 dimensions.
Hopefully my description is clear enough.
Any help would be most appreciated.
Let sphere is described by equation (where cx, cy, cz is sphere center, R is radius)
(x-cx)^2+(y-cy)^2+(z-cz)^2 = R^2
Point P0, direction vector is dir, so parameteric ray equation is
R = P0 + t * dir
or in coordinates
x = p0.x + t * dir.x
y = p0.y + t * dir.y
z = p0.z + t * dir.z
Substitute these expressions into sphere equation, solve resulting quadratic equation for unknown parameter t.
(p0x+t*dirx-cx)^2+.... = R^2
p0x^2+t^2*dirx^2+cx^2+2*p0x*t*dirx-2*p0x*cx-2*t*dirx*cx +... = R^2
t^2*(dirx^2+diry^2+dirz^2) +
t*(2*p0x*dirx-2*dirx*cx+2*p0y*diry-2*diry*cy+2*p0z*dirz-2*dirz*cz)
+ (p0x^2+p0y^2+p0z^2+cx^2+cy^2+cz^2-R^2) = 0
This is quadratic equation for unknown t
You might get 0, 1, or 2 solution for cases: ray does not intersect sphere, ray touches sphere, ray (line) intersects sphere in two points. For point P0 inside sphere one root will be negative, ignore it.
After that put t value into coordinate equations and get intersection point.
I can't figure out a way to do this. I have a list of vector2 points and I need all the points which are inside that polygon with a x distance.
So I have a List of Green points and looking for a List of Red points that have a x distance from respective green points.
I am thinking of getting 2 imaginary points, 1 unity towards the previous and next point.
Then moving towards the center of that 2 points by x distance. But then if the inter angle is not 90 then it will move outside of the polygon.
Vector2 me = point; // point
Vector2 next = n_point; // on left
Vector2 previous = p_point; // on right
//Debug.DrawLine(me, (me - next), Color.green);
// 2 points ep1 & ep2
Vector2 center = Vector2.Lerp(ep1,ep2, 0.5f);
Vector2 finalpoint = Vector2.Lerp(me,center,0.1f); //move towards center
I think I am overthinking this. Is there a super-easy way to do this?
Assuming that all the edges are either horizontal or vertical I would simply consider each possible case separately.
Get the direction vectors.
Vector2 from = me - previous;
Vector2 to = next - me;
I also assume that there is always a turn. I.e., if from is horizontal, then to is vertical and vice versa. Either x or y is 0.0f and the other coordinate is not zero.
I also assume that the x-axis points to the right and the y-axis upwards.
Assuming points are listed clock-wise.
float x, y;
if (from.x > 0.0f) { // from points to the right
y = me.y - distance;
if (to.y > 0.0f) x = me.x + distance else x = me.x - distance;
} else if (from.x < 0.0f) { // from points to the left
y = me.y + distance;
if (to.y > 0.0f) x = me.x + distance else x = me.x - distance;
} else if (from.y > 0.0f) { // from points upwards
x = me.x + distance;
if (to.x > 0.0f) y = me.y - distance else y = me.y + distance;
} else { // from.y < 0.0f, points downwards
x = me.x - distance;
if (to.x > 0.0f) y = me.y - distance else y = me.y + distance;
}
Vector2 me_inner = new Vector2(x, y);
I hope I got all the signs right.
There are two methods that spring to mind
Option1:
For each line define a normal, i.e. a perpendicular line pointing outward
Define a normal for each vertex as the average of the normals of the lines the vertex is part of.
Move the vertex X units along the normal.
This is fairly easy to implement, but may have problems with self-intersection for some kinds of geometry.
Option2:
For each line define a normal, i.e. a perpendicular line pointing outward
Move each line-segment X Units along the normal.
for each sequential pair of line segments determine if:
the two line segments intersect, if so, use the intersection point as the vertex. i.e. add the intersection point into your point-list.
If they do not intersect, insert a new line segment between the start and end point of the lines. i.e. Insert both start and end vertex to your point-list.
This should handle self-intersection better, but there might still be problem-cases. And it a bit more cumbersome to implement. It somewhat depend on how exact you need the new line positioned, and well it should handle different kinds of geometry.
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 have a plane defined by a normal vector and another normalalised direction vector that is moving along that plane, both in 3D space.
I'm trying to figure out how to project that normal direction 3D vector onto the plane such that it ends up being a 2D vector with x/y coordinates.
It sounds like you need to find the angle between the direction vector and the plane. The size of the projection is going to scale with the cosine of that angle. Since the normal vector of the plane is perpendicular, I think you can find the sine between the normal vector and your direction vector.
The angle between the two vectors is given by the dot product of the vectors over the magnitudes multiplied together. That gives us our theta. Take the sin of theta, and we have the scaling factor (I'll call it s)
Next, you need to define unit size vectors on the plane to project onto. It's probably easiest to do this by setting one of the unit vectors in the direction of the projection to move forward...
If you set the unit vector in the direction of the projection, then you know the length of the projection in that unit space by using the scaling factor and multiplying by the length of the vector.
After that, with the unit vector, multiply in the length and find your vector relative to your normally defined xyz axis.
I hope this helps.
Try something like this. I wrote a paper on this exact method a while ago and can provide you with a copy if you would like.
PointF Transform32(Point3 P)
{
float pX = (float)(((V.J * sxy) - V.I * cxy) * zoom);
float pY = (float)(((V.K * cz) - (V.I * sxy * sz) - (V.J * sz * cxy)));
return new PointF(Origin.X + pX, Origin.Y - pY);
}
cxy is the cosine of the x-y camera angle, measured in radians from the positive x-axis on the xy plane.
sxy is the sine of the x-y camera angle.
cz is the cosine of the z camera angle, measured in radians from the x-y plane (so the angle is zero if the camera rests on that plane).
sz is the sine of the z camera angle.
Alternatively:
Vector3 V = new Vector3(P.X, P.Y, P.Z);
Vector3 R = Operator.Project(V, View);
Vector3 Q = V - R;
Vector3 A = Operator.Cross(View, zA);
Vector3 B = Operator.Cross(A, View);
int pY = (int)(Operator.Dot(Q, B) / B.GetMagnitude());
int pX = (int)(Operator.Dot(Q, A) / A.GetMagnitude());
pY and pX should be your coordinates. Here, vector V is the position vector of the point in question, R is the projection of that vector onto your viewing vector, Q is the component of V orthogonal to the viewing Vector, A is an artificial X-axis formed by the cross-product of the viewing vector with the vector (0,0,1), and B is an artificial Y-axis formed by the cross product of A and (0,0,1).
It sounds like what you're looking for is something like a simple rendering engine, similar to this, which used the above formulae:
Hope this helps.
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