1
\$\begingroup\$

I am struggling to achieve the following with C# (in Unity). I need to find the position of the points that lay at a given distance (let's say 1m) from the corners of a rectangle, in a way that the segment between these points and the given rectangle corners form 135 degrees with the corresponding sides the of rectangle - no matter where the rectangle goes, what is its rotation or what is its size. Like the following:

enter image description here

So, using the picture above as reference, it means that the red dotted line has always the same length and divides the angle ABC in the middle. So, the angle of the line with AB is 135 and with BC is also 135.

One of the ways to achieve that is the following code:

    Vector3 dotpos = rectangle.transform.TransformPoint(rectangle.transform.localPosition.x+1.5F,0,rectangle.transform.localPosition.z+1.5F);
    bluedot.transform.position = dotpos ; 

The problem is that in such solution the distance between the blue dot and the corner of the rectangle is not independent of the scale of the rectangle. It means, the greater the rectangle, the blue dot goes more distant from the corner.

Can anybody tell what is the most efficient way to achieve what I want, i.e. so the position of the blue dot is always at the same distance from the corner no matter the scale, position or rotation of the rectangle?

PS: blue dot is not an object that can be made child of the rectangle.

\$\endgroup\$
  • \$\begingroup\$ Do you know the position of corners A,B and C? \$\endgroup\$ – wondra Oct 4 '15 at 22:57
  • \$\begingroup\$ Thanks for your comment. I know the world position of all 4 corners. \$\endgroup\$ – MAnd Oct 4 '15 at 23:37
2
\$\begingroup\$

Assuming you know world position of corner B:

vec3 dotpos = B - (rectangle.transform.forward + rectangle.transform.right).Normalize() * distance;

note: did not checked up and right vectors are the "correct" ones(direction of AB and BC), if not just try one of the other pairs.
You can also replace normalization with multiplication by constant of value 1/sqrt(2) because you know the length of hypotenuse of unit square.
The -(rectangle.transform.up + rectangle.transform.right) is getting vector "between" AB and BC direction, minus is for inverting. It is effectively the same as computing angles.

\$\endgroup\$
  • \$\begingroup\$ It worked great. About the normalization, so I suppose that it would be more efficient to declare a "float normalizer = 1/Mathf.Sqrt(2)" and then multiple the pairs by "normalizer" instead of calling for them the function ".Normalize()". Is that right? Lastly, let me just register here for future reference what pairs worked for me: "transform.right + transform.forward", "forward - right", "right - forward" and "- right - forward". \$\endgroup\$ – MAnd Oct 5 '15 at 23:09
  • \$\begingroup\$ @MAnd I am not sure what you mean by "pairs" but this is what I had in mind: B - (rectangle.transform.up + rectangle.transform.right) * (normalizer * distance); but smart compiler should produce same code for both options anyway. As for pairs: the first one is the "correct" one(editing answer), other two that happens the be correct ones too surprises me a bit - they should be at 45 and 225. I will be glad if anyone explained this to me. \$\endgroup\$ – wondra Oct 5 '15 at 23:32
  • \$\begingroup\$ By "pairs" I meant the same as you in the answer: right+forward, forward+up, etc. The pairs of combinations. But anyway, that's exactly what I did in the end: I pre-declared a normalizer for efficiency and multiplied the (right+forward) by it and then by the distance. This is by far the faster solution. Thanks! \$\endgroup\$ – MAnd Oct 5 '15 at 23:35
  • \$\begingroup\$ @MAnd Btw, dont forget to make your normalizer const - otherwise it might want to fetch the value every time. But then again, those differences really are irrelevant. \$\endgroup\$ – wondra Oct 6 '15 at 0:08
  • \$\begingroup\$ Anyway that's a good practice detail for this type of situation, that I was indeed forgetting. Thanks! \$\endgroup\$ – MAnd Oct 6 '15 at 0:30
1
\$\begingroup\$

I would do this using vectors and angles. You can get the angle that line segment AB makes with the x-axis, by doing the following:

angleAB = atan2(B.y - A.y, B.x - A.x);

You can add 45° (or π/4 radians) to that angle and project a point from B that is the length you desire by doing this:

newAngle = angleAB + (M_PI / 4.0);
newPoint.x = B.x + length * cos(newAngle);
newPoint.y = B.y + length * sin(newAngle);
\$\endgroup\$
  • \$\begingroup\$ There is no need for goniometric functions, they are often slow and inaccurate(not that it would matter here, but I consider overusing them bad practice). \$\endgroup\$ – wondra Oct 4 '15 at 23:19
  • \$\begingroup\$ Thanks for the answer. However, I was thinking the same as @wondra, so which is the more efficient way of doing that? I will edit the question to include the demand for efficiency, since in fact I will be doing that calculation for several thousand points. \$\endgroup\$ – MAnd Oct 4 '15 at 23:50
  • \$\begingroup\$ @MAnd The speed does not really matter unless you have several millions of those object (or it not at all), likewise the accuracy does not matter unless you are using it for spaceship navigation. \$\endgroup\$ – wondra Oct 5 '15 at 1:15
  • \$\begingroup\$ Speed is going to depend on usage. The answer from @Igor S. has an allocation, which also could be a time sink depending on how often it's called and other conditions. If you're concerned about that, you should profile each of the methods mentioned here and see which is fastest for your case. \$\endgroup\$ – user1118321 Oct 5 '15 at 4:12
  • \$\begingroup\$ @user1118321 my point why overusing goniometric functions can be bad: Imagine you were solving (1/3)x = 3 on paper, using goniometric function is like using a book to look up value of 1/3 (book said 0.33), replacing it the original equation: 0.33x = 3 and then proceeding to solve it. It lead to good-enough solution, its simple, but it can introduce rounding errors and performs eager evaluation of values you might not even need in the end. \$\endgroup\$ – wondra Oct 5 '15 at 23:49
1
\$\begingroup\$

You can do it like this:

bluedot.transform.localPosition = rectangle.transform.localPosition;
bluedot.transform.localRotation = rectangle.transform.localRotation;
bluedot.transform.localScale = Vector3.one;

Vector3 dotpos = bluedot.transform.TransformPoint(1.5f + rectangle.localScale.x / 2, 0.0f, 1.5f + rectangle.localScale.z / 2);
bluedot.transform.position = dotpos;
bluedot.transform.rotation = Quaternion.identity;

Here I use bluedot's transform to hold temporary values, because Unity doesn't allow using transforms without gameobjects. Rectangle.localScale.x / 2 is half the rectangle width... In the end I override temporary values with new ones. Hope you get the point.

\$\endgroup\$
  • \$\begingroup\$ Fixed the allocation thanks to user1118321. @MAnd, my example can be easily tweaked to work in 3d or in other plane (eg, XOY) if you'll want it in future; also it uses exactly the same approach as your question's code. \$\endgroup\$ – Igor S. Oct 5 '15 at 18:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.