从三个向量和给定的半径计算 3D 弧

Calculate 3D arc from three vectors and a given radius

如何在两条 3D 线相交处添加圆弧?

已知:半径,P1,P2,P3。

我需要的:Pa(圆弧起点3D),Pb(圆弧终点3D),Pc(圆弧中心点3D),

        double radius = 20;
        Vector3D P1 = new Vector3D(341.21, 227.208, 193.38);
        Vector3D P2 = new Vector3D(360.78, 85.34, 245.723);
        Vector3D P3 = new Vector3D(614.64, 85.34, 150.80);

        Vector3D P2P1 = new Vector3D(); 
        P2P1 = P1 - P2;


        Vector3D P2P3 = new Vector3D();
        P2P3 = P3 - P2;

我在 math.stackexchange 上找到了这个答案,但我不知道所有的数学符号 https://math.stackexchange.com/questions/2343931/how-to-calculate-3d-arc-between-two-lines

更新#1:

感谢@Spektre 我能够像这样解决这个问题

不得不使用新的 CAD 模型,旧的丢失了。

using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Media.Media3D;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            double r = 0.5;
            Vector3D P1 = new Vector3D(1.04004, 1.37919, 1.31332);
            Vector3D P2 = new Vector3D(2.78928, 2.34881, 1.31332);
            Vector3D P3 = new Vector3D(2.66790, 2.56780, 0.34518);
            Vector3D n = Vector3D.CrossProduct(P2 - P1, P3 - P1);
            n.Normalize();

            Vector3D d12 = new Vector3D();
            Vector3D d23 = new Vector3D();

            Vector3D ttt = Vector3D.CrossProduct(P2 - P1, n);
            Vector3D jjj = Vector3D.CrossProduct(P3 - P2, n);
            ttt.Normalize();
            jjj.Normalize();
            d12 = +- r * ttt;
            d23 = +- r * jjj;

            Vector3D A12 = new Vector3D();
            Vector3D B12 = new Vector3D();
            Vector3D A23 = new Vector3D();
            Vector3D B23 = new Vector3D();

            // Result 
            // One line displaced by radius
            A12 = P1 + d12;
            B12 = P2 + d12;
            // One line displaced by radius
            B23 = P2 + d23;
            A23 = P3 + d23;

            line3D line1 = new line3D( A12,  B12 ) ;
            line3D line2 = new line3D( A23,  B23 ) ;
            line3D ArcCenter = closest( line1, line2 );

            // CAD reference model,  Arc Center point3D   2,29128 2,21590 0,82925
            // Debug result 
            //center.dp { 0; 0; 0}
            //center.p0 { 2,29127973078106; 2,21590093975731; 0,829246317165185}
            //center.p1 { 2,29127973078106; 2,21590093975731; 0,829246317165185}


            Vector3D C = new Vector3D(ArcCenter.p0.X, ArcCenter.p0.Y, ArcCenter.p0.Z);

            Vector3D Pa = new Vector3D();
            Pa = C - d12;

            Vector3D Pb = new Vector3D();
            Pb = C - d23;

            Vector3D u = new Vector3D();
            u = Pa - C;

            Vector3D v = new Vector3D();
            v = Vector3D.CrossProduct(u, n);

            Vector3D d = new Vector3D();
            d = -d23 / (r * r);

            double ang; 
            ang = Math.Atan2(Vector3D.DotProduct(d, v), Vector3D.DotProduct(d, u));
            double a;            
            double da;
            int i;
            Collection<Vector3D> Vector3DList = new Collection<Vector3D>();

            for (a = 0.0, da = ang * 0.1, i = 0; i <= 10; a += da, i++)
            {
                Vector3DList.Add(C + u * Math.Cos(a) + v * Math.Sin(a));

            }
        }



        public line3D closest(line3D l0, line3D l1)
        {
            Vector3D u = l0.p1 - l0.p0;
            Vector3D v = l1.p1 - l1.p0;
            Vector3D w = l0.p0 - l1.p0;
            double a = Vector3D.DotProduct(u, u);       // always >= 0
            double b = Vector3D.DotProduct(u, v);
            double c = Vector3D.DotProduct(v, v);       // always >= 0
            double d = Vector3D.DotProduct(u, w);
            double e = Vector3D.DotProduct(v, w);
            double D = a * c - b * b;                   // always >= 0
            double t0;
            double t1;

            // compute the line3D parameters of the two closest points
            t0 = (b * e - c * d) / D;
            t1 = (a * e - b * d) / D;
            line3D r = new line3D(l0.p0 + l0.dp * t0, l1.p0 + l1.dp * t1);
            return r;
        }
        public class line3D
        {
            // cfg
            public Vector3D p0 = new Vector3D();
            public Vector3D p1 = new Vector3D();

            // computed
            public double l = 0;
            public Vector3D dp = new Vector3D();

            public line3D(Vector3D _p0, Vector3D _p1)
            {
                p0 = _p0;
                p1 = _p1;
                compute();
            }
            void compute()
            {
                dp = p1 - p0;
                l = dp.Length;
            }
        }
    }
}

假设圆弧是圆形的(您的图像显示一些曲线甚至不像椭圆,所以它很可能只是 BEZIER)?

无论如何,圆弧将与 P1,P2,P3 处于同一平面,因此您可以使用基向量将问题转换为 2D ...或者您可以使用向量数学直接在 3D 中完成,我认为这个:

  1. 将直线 P1P2P2P3 向内移动半径 r

    所以你需要平面 P1P2P3 的法向量,例如:

    n = normalize(cross(P2-P1,P3-P1))
    

    现在只需创建位移矢量(极性取决于您的缠绕规则)

    d12 = +/- r*normalize(cross(P2-P1,n))
    d23 = +/- r*normalize(cross(P3-P2,n))
    

    并置换

    A12 = P1 + d12
    B12 = P2 + d12
    A23 = P2 + d23
    B23 = P3 + d23
    
  2. 计算位移线 A12B12A23B23

    的交点

    这将为您提供圆弧的圆心 C。这是从我的 :

    中获取的直线相交计算
     line3D closest(line3D l0,line3D l1)
         {
         vec3 u=l0.p1-l0.p0;
         vec3 v=l1.p1-l1.p0;
         vec3 w=l0.p0-l1.p0;
         float a=dot(u,u);       // always >= 0
         float b=dot(u,v);
         float c=dot(v,v);       // always >= 0
         float d=dot(u,w);
         float e=dot(v,w);
         float D=a*c-b*b;        // always >= 0
         float t0,t1;
         point3D p;
         line3D r,rr;
         int f;                  // check distance perpendicular to: 1: l0, 2: l1
         f=0; r.l=-1.0;
         // compute the line3D parameters of the two closest points
         if (D<acc_dot) f=3;    // the lines are almost parallel
         else{
             t0=(b*e-c*d)/D;
             t1=(a*e-b*d)/D;
             if (t0<0.0){ f|=1; t0=0.0; }
             if (t0>1.0){ f|=1; t0=1.0; }
             if (t1<0.0){ f|=2; t1=0.0; }
             if (t1>1.0){ f|=2; t1=1.0; }
             r=line3D(l0.p0+(l0.dp*t0),l1.p0+(l1.dp*t1));
             }
         // check perpendicular distance from each endpoint marked in f
         if (int(f&1))
             {
             t0=0.0;
             t1=divide(dot(l0.p0-l1.p0,l1.dp),l1.l*l1.l);
             if (t1<0.0) t1=0.0;
             if (t1>1.0) t1=1.0;
             rr=line3D(l0.p0+(l0.dp*t0),l1.p0+(l1.dp*t1));
             if ((r.l<0.0)||(r.l>rr.l)) r=rr;
             t0=1.0;
             t1=divide(dot(l0.p1-l1.p0,l1.dp),l1.l*l1.l);
             if (t1<0.0) t1=0.0;
             if (t1>1.0) t1=1.0;
             rr=line3D(l0.p0+(l0.dp*t0),l1.p0+(l1.dp*t1));
             if ((r.l<0.0)||(r.l>rr.l)) r=rr;
             }
         if (int(f&2))
             {
             t1=0.0;
             t0=divide(dot(l1.p0-l0.p0,l0.dp),l0.l*l0.l);
             if (t0<0.0) t0=0.0;
             if (t0>1.0) t0=1.0;
             rr=line3D(l0.p0+(l0.dp*t0),l1.p0+(l1.dp*t1));
             if ((r.l<0.0)||(r.l>rr.l)) r=rr;
             t1=1.0;
             t0=divide(dot(l1.p1-l0.p0,l0.dp),l0.l*l0.l);
             if (t0<0.0) t0=0.0;
             if (t0>1.0) t0=1.0;
             rr=line3D(l0.p0+(l0.dp*t0),l1.p0+(l1.dp*t1));
             if ((r.l<0.0)||(r.l>rr.l)) r=rr;
             }
         return r;
         }
    

    每条线都有端点 p0,p1 和 displacement/direction dp=p1-p0 以及长度 l=|dp|。函数 returns 2 个彼此最近的点,一个属于线 l0,另一个属于 l1,因此如果它们匹配(它们的距离接近于零或更小),它们就是交点 C我们正在寻找。如果不是,则线条要么平行,要么太短或彼此远离(或者您为一个或两个位移选择了错误的极性)

  3. 计算弧端点

    Pa = C - d12
    Pb = C - d23
    
  4. 计算弧的基础向量u,v

    例如:

    u = Pa - c
    v = cross(u,n)
    
  5. 为你计算弧的边角

    因为我直接选择 u 作为圆弧的起点,我们只需要结束角

    d = -d23/(r*r)
    ang = atan2(dot(d,v),dot(d,u))
    // if (ang>M_PI) ang-=2.0*M_PI    // use smaller arc however usual atan2 is in <-PI,+PI> range so no need for this
    
  6. 渲染你的弧线

    弧上的任何点都将像这样计算:

    p(a) = C + u*cos(a) + v*sin(a)
    a = <0 , ang>
    

如果您不熟悉 3D 矢量数学运算(点、叉、..),您可以在此处找到它们的方程式和实现(查找 [Edit2]):