几何向量:空间圆

      记录一下空间圆的生成功能。
      已知空间两端点f和t,求f所在平面F,半径为r的圆,如下:
在这里插入图片描述
      求动点p(x,y,z)则可以得到圆,那么开始构建相应的方程组,首先计算平面F,如下:
在这里插入图片描述
      接下来通过向量fp模长等于r,构建方程:
在这里插入图片描述
      再次通过三角形tfp为直角三角形,所以向量tp模长的平方=r的平方+向量ft模长的平方,再次构建方程:
在这里插入图片描述
      但是我们利用上面三个表达式无法解出解,因为后两个二次方程相减得到的依旧是三元方程,和第一个方程类似,所以最多只能将x、y、z中任意一个作为参数求得其余两个,如下:
在这里插入图片描述
      当然哪怕我们不去求解方程组,也知道p(x,y,z)求不出来,因为p就是圆环上的动点,只存在无数多的解,但是我们只需要一个定点p,所以还需要一个明确的条件确定一个明确的p点。
      我们可以参考圆规,如下:
在这里插入图片描述
      假设我们三维空间中有一个圆规,圆规轴点为t,定轴为tf,动轴为tp1,所以动轴tp1与平面F相交就能确定定点p1,在通过fp1单位法向量*半径r得到定点p(x,y,z),那么用数学语言来描述:
      以端点t为起点,不与射线tf重合(且不与平面F平行)的任意射线tp1,与平面F相交于p1点,则能通过射线与平面相交计算得到定点p1,在通过法向量乘模长得到定点p。
      构建几何示意图:
在这里插入图片描述
      那么求空间非直线tf上任意点p2,即可得到求得点p。
      而任意点p2 = t + 非单位向量tf * 任意长度数值。
      任意点p2可以用f绕x(y或z)轴旋转θ>0°且θ<90°(如θ=30°)得到,如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpacePointAndCircle : MonoBehaviour
{
    
    
    public Transform startTrans;            //from
    public Transform endTrans;              //to
    [Range(0, 90f)]
    public float rotateAngle = 30f;

    void Start()
    {
    
    

    }


    void Update()
    {
    
    
#if UNITY_EDITOR
        Vector3 start = startTrans.position;
        Vector3 end = endTrans.position;
        Debug.DrawLine(start, end, Color.black);
        Vector3 p2 = RotateAroundXAxis(start, end, rotateAngle * Mathf.Deg2Rad);
        Debug.DrawLine(end, p2, Color.white);
#endif
    }

    /// <summary>
    /// 空间任意起终点
    /// 终点绕x轴旋转rad弧度
    /// </summary>
    /// <param name="start"></param>
    /// <param name="end"></param>
    /// <param name="rad"></param>
    /// <returns></returns>
    private Vector3 RotateAroundXAxis(Vector3 start, Vector3 end, float rad)
    {
    
    
        Matrix3x3 mat = new Matrix3x3();

        float cos = Mathf.Cos(rad);
        float sin = Mathf.Sin(rad);

        mat.m00 = 1;
        mat.m01 = 0;
        mat.m02 = 0;
        mat.m10 = 0;
        mat.m11 = cos;
        mat.m12 = -sin;
        mat.m20 = 0;
        mat.m21 = sin;
        mat.m22 = cos;

        Vector3 ret = mat * (start - end) + end;
        return ret;
    }
}

      效果如下:
在这里插入图片描述
      接下来我们计算射线tp2与平面F相交点p1,如下:
在这里插入图片描述
      射线与平面相交以前聊过,节省篇幅,我们直接运算就行了(不理解原理的可以返回几何向量栏目查看),如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpacePointAndCircle : MonoBehaviour
{
    
    
    public Transform startTrans;            //from
    public Transform endTrans;              //to
    [Range(0, 90f)]
    public float rotateAngle = 30f;

    void Start()
    {
    
    

    }


    void Update()
    {
    
    
#if UNITY_EDITOR
        Vector3 start = startTrans.position;
        Vector3 end = endTrans.position;
        Debug.DrawLine(start, end, Color.black);
        Vector3 p2 = RotateAroundXAxis(start, end, rotateAngle * Mathf.Deg2Rad);
        Debug.DrawLine(end, p2, Color.white);
        Vector3 p1 = RayLineCrossPanel(start, end, p2);
        Debug.DrawLine(p2, p1, Color.red);
#endif
    }

    /// <summary>
    /// 通过start end计算start所处平面F方程
    /// 通过end p2计算射线与平面F交点p1
    /// </summary>
    /// <param name="start"></param>
    /// <param name="end"></param>
    /// <param name="p2"></param>
    /// <returns></returns>
    private Vector3 RayLineCrossPanel(Vector3 start, Vector3 end, Vector3 p2)
    {
    
    
        //start = from
        //end = to
        //构建平面F方程参数
        Vector3 ft = end - start;
        float u = ft.x, v = ft.y, w = ft.z;
        float a = start.x, b = start.y, c = start.z;
        //构建射线tp2参数
        float sx = end.x;
        float sy = end.y;
        float sz = end.z;
        Vector3 ntp2 = (p2 - end).normalized;
        float dx = ntp2.x;
        float dy = ntp2.y;
        float dz = ntp2.z;
        //计算p1
        float n = ((u * a + v * b + w * c) - (u * sx + v * sy + w * sz)) / (u * dx + v * dy + w * dz);
        Vector3 p1 = end + n * ntp2;
        return p1;
    }

    /// <summary>
    /// 空间任意起终点
    /// 终点绕x轴旋转rad弧度
    /// </summary>
    /// <param name="start"></param>
    /// <param name="end"></param>
    /// <param name="rad"></param>
    /// <returns></returns>
    private Vector3 RotateAroundXAxis(Vector3 start, Vector3 end, float rad)
    {
    
    
        Matrix3x3 mat = new Matrix3x3();

        float cos = Mathf.Cos(rad);
        float sin = Mathf.Sin(rad);

        mat.m00 = 1;
        mat.m01 = 0;
        mat.m02 = 0;
        mat.m10 = 0;
        mat.m11 = cos;
        mat.m12 = -sin;
        mat.m20 = 0;
        mat.m21 = sin;
        mat.m22 = cos;

        Vector3 ret = mat * (start - end) + end;
        return ret;
    }
}

      效果如下:
在这里插入图片描述

      这样我们就得到了start(from)和p1,也就是平面F上两个坐标点。方便计算以start(from)为起点,半径r的圆了,如下:
在这里插入图片描述
      通过f + nfp1 * r就能得到p(x,y,z)了,如下:

        Vector3 p = start + (p1 - start).normalized * circleRadius;
        Debug.DrawLine(start, p, Color.blue);

      效果如下:
在这里插入图片描述

      既然我们得到了p(x,y,z),那么接下来就把圆gizmos出来,方法就是p(x,y,z)绕轴ft离散的旋转θ度可得,如下:
在这里插入图片描述
      这种圆绘制的方法我们以前聊过很多了,无非就是间隔的逆时针旋转计算离散的坐标数组,再相连绘制,如下:

 private void DrawCircleGizmos(Vector3 start, Vector3 end, Vector3 p)
    {
    
    
        Vector3[] posarr = CalculateCirclePoints(start, end, p, circleSegement);
        for (int i = 0; i < posarr.Length; i++)
        {
    
    
            Vector3 fp = posarr[i];
            Vector3 tp = posarr[(i + 1) % circleSegement];
            Debug.DrawLine(fp, tp, Color.yellow);
        }
    }

    /// <summary>
    /// 算出圆环上所有离散坐标点
    /// </summary>
    /// <param name="start"></param>
    /// <param name="end"></param>
    /// <param name="p"></param>
    /// <param name="sege"></param>
    /// <returns></returns>
    private Vector3[] CalculateCirclePoints(Vector3 start, Vector3 end, Vector3 p, int sege)
    {
    
    
        Vector3[] posarr = new Vector3[sege];
        posarr[0] = p;
        Vector3 naxis = (end - start).normalized;
        float segerad = 2f * Mathf.PI / (float)sege;
        for (int i = 1; i < sege; i++)
        {
    
    
            float rad = segerad * i;
            Vector3 segepos = RotateAroundAnyAxis(start, p, naxis, rad);
            posarr[i] = segepos;
        }
        return posarr;
    }

    /// <summary>
    /// p(x,y,z)点绕start为起点的任意坐标轴旋转后的坐标
    /// </summary>
    /// <param name="start"></param>
    /// <param name="naxis"></param>
    /// <param name="rad"></param>
    /// <returns></returns>
    private Vector3 RotateAroundAnyAxis(Vector3 start, Vector3 p, Vector3 naxis, float rad)
    {
    
    
        float n1 = naxis.x;
        float n2 = naxis.y;
        float n3 = naxis.z;

        //获取p相对start的本地坐标
        p -= start;

        float sin = Mathf.Sin(rad);
        float cos = Mathf.Cos(rad);

        Matrix3x3 mat = new Matrix3x3();

        mat.m00 = n1 * n1 * (1 - cos) + cos;
        mat.m01 = n1 * n2 * (1 - cos) - n3 * sin;
        mat.m02 = n1 * n3 * (1 - cos) + n2 * sin;

        mat.m10 = n1 * n2 * (1 - cos) + n3 * sin;
        mat.m11 = n2 * n2 * (1 - cos) + cos;
        mat.m12 = n2 * n3 * (1 - cos) - n1 * sin;

        mat.m20 = n1 * n3 * (1 - cos) - n2 * sin;
        mat.m21 = n2 * n3 * (1 - cos) + n1 * sin;
        mat.m22 = n3 * n3 * (1 - cos) + cos;

        //绕轴旋转后,处理成世界坐标
        Vector3 px = mat * p + start;

        return px;
    }

      最终效果如下:
在这里插入图片描述      好,今天就写到这里。

猜你喜欢

转载自blog.csdn.net/yinhun2012/article/details/122005596