4
votes

I would like to randomly select a vector originating at point P, such that the line formed alongside this vector is tangent to the surface of some sphere. To do this, I need a collection of all points S forming a circle on the sphere, such that the line SP is tangent to the sphere at point S. Then, given this information, I can select one point S', and create a direction vector from point P to point S'.

I would like to do this in Unity. I don't have much preference over how the vector is created itself, as long as it can be randomised and points to a point on the abovementioned circle. I believe an ideal solution would consider an angle θ ∈ [0, 2π] which can be randomised to give a vector from the origin of the circle (not the sphere) to the associated S' point.

I would appreciate a solution in C#, but I am happy with other languages too. Please do note that while the mathematical solutions are appreciated, I am specifically looking for implementation details, as I am not very fluent with Unity engine, their coordinate system and vector operations yet.

Visualisation below:

Visualisation

1
you know the distance between the point P and the center of the sphere? You know the diameter of the sphere?Frenchy
@Frenchy Yes, I do - I know all of it.Kacper Floriański
As soon as you know the circle: docs.unity3d.com/ScriptReference/Random-insideUnitCircle.html and normalize it .. in general this sounds more like a math question thoughderHugo
upvote for such a cool problem!rustyBucketBay

1 Answers

1
votes

The solution in Unity is as follows:

  1. Compute the circle's center point
  2. Compute the circle's radius
  3. Create a projection using a randomly selected point in 3D coordinates and point P
  4. Use the center point and the radius to position the point, which results in finding S', such that PS' is tangent to the sphere
using UnityEngine;

public class Script : MonoBehaviour
{
    private readonly float _accuracy = 0.1f;
    private readonly int _vectorsCount = 1000;
    private readonly Vector3 _point = new Vector3(600, 600, 600);
    private readonly float _sphereRadius = 500f;

    private void Start()
    {
        // This value will be used to calculate both the circle coordinates and its radius.
        var quadraticSum = Mathf.Pow(_point.x, 2) + Mathf.Pow(_point.y, 2) + Mathf.Pow(_point.z, 2);

        // Find out coordinates of the circle created by the intersection of the plane with the sphere
        Vector3 circleCenter;
        circleCenter.x = _point.x * Mathf.Pow(_sphereRadius, 2) / quadraticSum;
        circleCenter.y = _point.y * Mathf.Pow(_sphereRadius, 2) / quadraticSum;
        circleCenter.z = _point.z * Mathf.Pow(_sphereRadius, 2) / quadraticSum;

        // Find out radius of the above circle
        var circleRadius = _sphereRadius * Mathf.Sqrt(quadraticSum - Mathf.Pow(_sphereRadius, 2)) /
                           Mathf.Sqrt(quadraticSum);

        /*
         * At this point, we can start drawing - let's draw:
         *
         *   - the point using red colour
         *   - the sphere using blue colour
         *   - the circle using green colour
         *
         * Below assumes center of the sphere is at (0, 0, 0)
         */
        Debug.DrawLine(Vector3.zero, _point, Color.red, 1000);
        DrawSphere();
        DrawCircle(circleCenter, circleRadius);
    }

    private void DrawSphere()
    {
        for (var theta = -Mathf.PI; theta < Mathf.PI; theta += _accuracy)
        {
            for (var phi = -Mathf.PI; phi < Mathf.PI; phi += _accuracy)
            {
                var ray = new Vector3(
                    _sphereRadius * Mathf.Sin(theta) * Mathf.Cos(phi),
                    _sphereRadius * Mathf.Sin(theta) * Mathf.Sin(phi),
                    _sphereRadius * Mathf.Cos(theta)
                );
                Debug.DrawLine(Vector3.zero, ray, Color.blue, 1000);
            }
        }
    }

    private void DrawCircle(Vector3 center, float radius)
    {
        for (int i = 0; i < _vectorsCount; i++)
        {
            // Since I wanted random vectors, I am repeatedly drawing a random vector on the circle
            var tangentPoint = Vector3.ProjectOnPlane(Random.insideUnitSphere, _point).normalized * radius + center;
            Debug.DrawLine(Vector3.zero, tangentPoint, Color.green, 1000);
            //Debug.DrawLine(_point, tangentPoint, Color.cyan, 1000);
        }
    }
}

See the screenshots for visualisation: Angle1

Angle3

Cone

Here is an angle with the red line drawn after the sphere and the circle are drawn, to see the center of the sphere:

AngleBonus