- Home /
Clamp Vector direction to one axis
Hello, I'm struggling a bit with vector3 operations. I have a rectangle (called A) and inside that rectangle a set of cubes (let's call each cube B) can be placed in the border of the rectangle A. I have managed to get the directoin of the border rectangle for the room (simple dir = (b.pos - a.pos).normalized). Here is what i got at the moment:
The black dot is the A rectangle position, and the purple lines it's the direction each B square is facing from the rectangle A. But what i want it's a Vector direction like the red arrow, where each direction is clamped/align to one axis only, so we know which part of the rectangle is placed. I need this to be programatically as in a later stage i want to add many different shapes of A and i need to know the face they are placed.
var dir = (d.transform.position - r.transform.position).normalized * 5f;
Debug.DrawLine(d.transform.position, d.transform.position + dir, Color.magenta);
Answer by Bunny83 · Aug 16, 2020 at 01:07 PM
At no point have you included the dimensions of your rectangle, so anything you do won't work without those information. The key is simply to figure out on which side of the rectangle you are. This can easily be done by first scaling space so the rectangle becomes a square. This can be easily done with the aspect ratio of your rectangle. Once you have the positions of your objects in the "fixed" space, it's just a matter of some nested if statements. If the absolute value of the x coordinate is greater than the absolute value of the y coordinate, the normal is either the right or left vector, if it's the other way round the normal would be either the up or down vector. By looking at the sign of the components you can determine which case actually applies. This trivial case of course only applies for an axis aligned rectangle. However even with a rotated one, if you have the rotation quaternion available it's not that much harder.
public static Vector3 GetRectangleNormal(Vector3 aRectCenter, Vector2 aRectSize, Quaternion aRectRot, Vector3 aPosition)
{
Vector3 vec = aPosition - aRectCenter;
vec = Quaternion.Inverse(aRectRot) * vec;
vec.y *= aRectSize.x / aRectSize.y;
Vector3 normal;
if (Mathf.Abs(vec.x) > Mathf.Abs(vec.y))
normal = Vector3.right * Mathf.Sign(vec.x);
else
normal = Vector3.up * Mathf.Sign(vec.y);
return aRectRot * normal;
}
To break it down
// worldspace direction from the rect center to the position
Vector3 vec = aPosition - aRectCenter;
// if the rectangle is rotated, apply the inverse to get a local space direction
vec = Quaternion.Inverse(aRectRot) * vec;
// make space square by fixing with the aspect ratio.
vec.y *= aRectSize.x / aRectSize.y;
// the x component is greater than the y component, we are on one of the sides (left or right)
// otherwise we are on the top or bottom edge
if (Mathf.Abs(vec.x) > Mathf.Abs(vec.y))
// in case we are on one of the sides, thus the localspace normal is simply (1,0,0). We multiply
// by the sign of the x component to get (-1,0,0) if it's on the left side
normal = Vector3.right * Mathf.Sign(vec.x);
// same logic as above but this time the normal is (0,1,0) and the sign of the y component
// decides if it's up or down.
normal = Vector3.up * Mathf.Sign(vec.y);
// finally we rotate our local space normal into worldspace
return aRectRot * normal;
This should work for any rectangle that is defined in the local x-y-space with an arbitrary 3d rotation.
If you don't need the rotation, you can simply remove the quaternion and remove the line 4 completely and at the end just return normal.