2021-12-28【Math】空间圆切线计算

2021-12-28  本文已影响0人  持刀的要迟到了

在空间三维坐标系下的圆、直线和平面拟合九磅十五便士的博客-CSDN博客_三维坐标系中圆的方程
geometry - Tangents to a circle in 3D - Mathematics Stack Exchange

2D空间计算:

把点平移到圆心,以圆心为原点计算,最后再根据圆心进行偏移量偏移。

        // this approach is more geometrical and less algebraic than approach 1,
        // and far more stable. thanks to Mike Plotz for suggesting this direction.
        // https://answers.unity.com/questions/1617078/finding-a-tangent-vector-from-a-given-point-and-ci.html
        public static bool CircleTangents_2(Vector2 center, float r, Vector2 point, 
            ref Vector2 tanPosA, ref Vector2 tanPosB)
        {
            point -= center;    // point = point - center :point = c->p 向量
                                // 也可以理解为,以center为圆心建立坐标系。

            float P = point.magnitude;

            // if p is inside the circle, there ain't no tangents.
            if (P <= r)
            {
                return false;
            }

            float a = r * r / P;    // r * sin(theta)
            float q = r * (float)System.Math.Sqrt((P * P) - (r * r)) / P;// r * cos(theta)

            Vector2 pN = point / P; // 单位向量
            Vector2 pNP = new Vector2(-pN.y, pN.x); // 垂直于上向量的单位向量
                                                    //Dot点乘:-xy + xy = 0
            Vector2 va = pN * a;    // 圆上交点

            tanPosA = va + pNP * q;
            tanPosB = va - pNP * q;

            tanPosA += center;
            tanPosB += center;

            return true;
        }

3D空间计算:

获得圆心所在平面的,法线朝向。获得xz平面的法线向量。然后把所有已知向量,
根据这两个法线向量进行旋转和位移转换。然后根据xz上的值进行2d计算,计算后,
再旋转和位移转换回去。

using SGF.Utility;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpaceCicleTest : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        Vector3 camPos = transform.position;
        Vector3 centerPos = center.position;

        // 绘制竖直方向两条切线
        // 投影到 normal = y 平面上

        // 原始切线
        Vector3 normalDir = Vector3.Cross(centerPos - camPos, Vector3.up).normalized;
        Quaternion rotOffset = Quaternion.FromToRotation(normalDir, Vector3.up);
        
        Vector3 camChangedPos = rotOffset * camPos;
        Vector3 centerChangedPos = rotOffset * centerPos;

        Debug.LogError(camChangedPos.y + "_" + centerChangedPos.y);

        Vector2 tanA = Vector2.zero, tanB = Vector2.zero;
        SGF.Utility.MathUtility.CircleTangents_2(new Vector2(centerChangedPos.x, centerChangedPos.z), circleRadius, new Vector2(camChangedPos.x, camChangedPos.z), ref tanA, ref tanB);


        Vector3 tana = new Vector3(tanA.x, centerChangedPos.y, tanA.y);
        Vector3 tanb = new Vector3(tanB.x, centerChangedPos.y, tanB.y);

        tana = Quaternion.Inverse(rotOffset) * tana;
        tanb = Quaternion.Inverse(rotOffset) * tanb;

        Debug.DrawLine(camPos, tana, Color.red);
        Debug.DrawLine(camPos, tanb, Color.red);
    }


    public Transform center;
    public float circleRadius = 0.8f;
    private void OnDrawGizmos()
    {
        if (center)
        {
            Gizmos.color = new Color(0.3f, 0.1f, 0.25f, 0.2f);
            Gizmos.DrawSphere(center.position, circleRadius);
        }
    }
}
上一篇 下一篇

猜你喜欢

热点阅读