【问题标题】:Calculate 3D arc from three vectors and a given radius从三个向量和给定的半径计算 3D 弧
【发布时间】:2023-02-13 01:07:41
【问题描述】:

如何在两条 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;
            }
        }
    }
}

【问题讨论】:

    标签: c# math vector trigonometry


    【解决方案1】:

    假设圆弧是圆形的(您的图像显示一些曲线甚至不像椭圆,所以它很可能只是 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。这是从我的C++ geometry engine 获取的直线交点计算:

       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和位移/方向dp=p1-p0和长度l=|dp|。该函数返回 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]):

    【讨论】:

    • 太好了,我会试一试:-) 圆弧是圆形的。
    • 第 1 步“替换你的线条”现在正在运行,步骤。 2. 计算位移线的交点,line3D(l0.p0 + (l0.dp * t0), l1.p0 + (l1.dp * t1));这是一种方法吗? line3D.p0 是 point3D 吗?
    • @LauritsS 是的......如果你查看几何引擎的链接,那里有完整的源代码,这样你就可以看到里面有什么......你可以使用你可以随意使用的任何线/线交点......我只是分享了我的如果你没有。你也可以使用轴/轴交点(那个更简单一点)
    • 好的,是的,我看到了,谢谢。
    • 好的,现在一切正常,哇! a = 4; 我以为可能是弧形分区。看起来现在是双角度; ang = Math.Atan2(Vector3D.DotProduct(d, v), Vector3D.DotProduct(d, u));双a;双哒;诠释我;集合<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)); }
    【解决方案2】:

    这个解决方案效果很好,但我有一个问题。有没有办法限制半径?所以基本上我需要限制圆弧将从较短线的最大半长开始的半径。 (P1P2 和 P2P3 行)。谢谢。

    【讨论】:

    猜你喜欢
    • 2014-05-12
    • 1970-01-01
    • 1970-01-01
    • 2012-05-28
    • 2018-08-10
    • 1970-01-01
    • 1970-01-01
    • 2018-07-28
    • 2013-04-05
    相关资源
    最近更新 更多