【问题标题】:C# : Find all points on an arc formed by 3 points (3D)C#:查找由 3 个点(3D)形成的弧上的所有点
【发布时间】:2020-03-12 14:26:09
【问题描述】:

我想找到位于由 3 个点组成的弧上的所有点的坐标。

首先,我的英语不太好,很抱歉语言有误。

点 1 : (toolPosition.x, toolPosition.y, toolPosition.z) 第2点:(TMid.transform.position.x,TMid.transform.position.y,TMid.transform.position.z) 第 3 点:(TEnd.transform.position.x, TEnd.transform.position.y, TEnd.transform.position.z)

我在 (x,y,z) 坐标中,困难在哪里。

我首先计算球心的坐标 (xc, yc, zc),该点有效。

之后,我计算了弧的长度和角度(点1到点3,经过点2)

//calcul des angles des points donnés sur la sphère
double tetaA = Math.Acos((toolPosition.z - zc) / rayon);
double tetaB = Math.Acos((TMid.transform.position.z - zc) / rayon);
double tetaC = Math.Acos((TEnd.transform.position.z - zc) / rayon);

double phiA = Math.Atan2((toolPosition.y - yc), (toolPosition.x - xc));
double phiB = Math.Atan2((TMid.transform.position.y - yc), (TMid.transform.position.x - xc));
double phiC = Math.Atan2((TEnd.transform.position.y - yc), (TEnd.transform.position.x - xc));
//longueur des arcs
double longStartEnd = rayon * Math.Acos(Math.Cos(tetaA) * Math.Cos(tetaC) + Math.Sin(tetaA) * Math.Sin(tetaC) * Math.Cos(phiC - phiA));
double longMidEnd = rayon * Math.Acos(Math.Cos(tetaB) * Math.Cos(tetaC) + Math.Sin(tetaB) * Math.Sin(tetaC) * Math.Cos(phiC - phiB));
double longStartMid = rayon * Math.Acos(Math.Cos(tetaA) * Math.Cos(tetaB) + Math.Sin(tetaA) * Math.Sin(tetaB) * Math.Cos(phiB - phiA));
//si le trajet dépasse les 180°
if (longStartMid > longStartEnd || ((float)(longStartEnd * 2) != (float)(2 * Math.PI * rayon) && (float)(longStartMid + longStartEnd + longMidEnd) == (float)(2 * Math.PI * rayon)))
    longStartEnd = 2 * Math.PI * rayon - longStartEnd;
//calcul du nombre de découpe à réaliser et de l'angle formé par chacune
int nbrDecoupe = (int)(longStartEnd * 100);
double angle = longStartEnd / rayon;
double angleBeta = angle / nbrDecoupe;

现在我真正的问题来了,找到弧上的所有点。

我尝试通过旋转矩阵来计算它们,这需要一个法线向量。

这是我的计算方法:

//recherche du vecteur normal du plan formé par les trois points
double xsm = TMid.transform.position.x - toolPosition.x, ysm = TMid.transform.position.y - toolPosition.y, zsm = TMid.transform.position.z - toolPosition.z; // vecteur entre le startPoint et le midPoint
double xse = TEnd.transform.position.x - toolPosition.x, yse = TEnd.transform.position.y - toolPosition.y, zse = TEnd.transform.position.z - toolPosition.z; // vecteur entre le startPoint et le endPoint
double z1 = zse / zsm;
if (Math.Sign(zsm) == Math.Sign(zse))
    z1 *= -1;
double y1 = yse / ysm;
if (Math.Sign(ysm) == Math.Sign(yse))
    y1 *= -1;
//vecteur normal
double aNormal = 1;
double bNormal = (z1 * xsm + xse) * -1 / (z1 * ysm + yse);
double cNormal = (y1 * xsm + xse) * -1 / (y1 * zsm + zse);
//vecteur normal unitaire
double ux, uy , uz; // ux² + uy² + uz² = 1
ux = 1 / Math.Sqrt(Math.Pow(aNormal, 2) + Math.Pow(bNormal, 2) + Math.Pow(cNormal, 2)) * aNormal * sens;
uy = 1 / Math.Sqrt(Math.Pow(aNormal, 2) + Math.Pow(bNormal, 2) + Math.Pow(cNormal, 2)) * bNormal * sens;
uz = 1 / Math.Sqrt(Math.Pow(aNormal, 2) + Math.Pow(bNormal, 2) + Math.Pow(cNormal, 2)) * cNormal * sens;

第一点是,有时(当圆弧在轴上时)bNormal 或 cNormal 除以 0。

第二点是我现在不知道如何确定向量的 sens (弧必须经过点 2)。

我这样做是为了找到感觉,但我知道这不是一个好的解决方案

int sens;
if ((TEnd.transform.position.y < toolPosition.y || TEnd.transform.position.z < toolPosition.z) && Math.Abs(TEnd.transform.position.y - toolPosition.y) > 0.00001)
    sens = -1;
else
    sens = 1;
if (Math.Abs(toolPosition.y - TEnd.transform.position.y) < Math.Abs(toolPosition.y - TMid.transform.position.y))
    sens *= -1;

根据所有这些数据,我计算了所有要点:

 double xL = toolPosition.x - xc, yL = toolPosition.y - yc, zL = toolPosition.z - zc;
 for (int i = 0; i < nbrDecoupe; i++)
 {
     //Coordonnées du nouveau point
     double tempX = xL, tempY = yL;
     // xL * (cos() + ux²*(1 - cos()))      + yL * (ux*uy*(1 - cos()) - uz*sin()) + zL * (ux*uz*(1 - cos()) + uy*sin())
     // xL * (uy*ux*(1 - cos()) + uz*sin()) + yL * (cos() + uy²*(1 - cos()))      + zL * (uy*uz*(1 - cos()) - ux*sin())
     // xL * (uz*ux*(1 - cos()) - uy*sin()) + yL * (uz*uy*(1 - cos()) + ux*sin()) + zL * (cos() + uz²*(1 - cos()))
     xL = tempX * (Math.Cos(angleBeta) + Math.Pow(ux, 2) * (1 - Math.Cos(angleBeta))) + tempY * (ux * uy * (1 - Math.Cos(angleBeta)) - uz * Math.Sin(angleBeta)) + zL * (ux * uz * (1 - Math.Cos(angleBeta)) + uy * Math.Sin(angleBeta));   
     yL = tempX * (uy * ux * (1 - Math.Cos(angleBeta)) + uz * Math.Sin(angleBeta)) + tempY * (Math.Cos(angleBeta) + Math.Pow(uy, 2) * (1 - Math.Cos(angleBeta))) + zL * (uy * uz * (1 - Math.Cos(angleBeta)) - ux * Math.Sin(angleBeta));   
     zL = tempX * (uz * ux * (1 - Math.Cos(angleBeta)) - uy * Math.Sin(angleBeta)) + tempY * (uz * uy * (1 - Math.Cos(angleBeta)) + ux * Math.Sin(angleBeta)) + zL * (Math.Cos(angleBeta) + Math.Pow(uz, 2) * (1 - Math.Cos(angleBeta)));
 }

我认为问题出在法向量上,有时不好。

所以,我需要帮助的两点是如何确定向量的 sens 和 calcul(实际上在所有情况下都不起作用)

如果有人可以帮助我,将非常感激。

【问题讨论】:

  • 你的弧是不是像二次贝塞尔曲线?
  • 一般而言,关于您的措辞:在数学上不可能在任何直线或圆弧上获得 所有个点,因为直线上的点数是无限..
  • 我知道弧有无穷大的点,这是因为我的 for 循环中有一个“nbrDecoupe”。我忘了把它放在问题中。

标签: c# unity3d math


【解决方案1】:

您可以找到圆心和圆弧半径为described here (another implementation)。这是 C++ 代码,但我认为翻译应该很明显(dot 是点积,.norm() 是向量的归一化)

void estimate3DCircle(Vector3D p1, Vector3D p2, Vector3D p3, Vector3D &c, double &radius)
{
    Vector3D v1 = p2-p1;
    Vector3D v2 = p3-p1;
    double v1v1, v2v2, v1v2;
    v1v1 = v1.dot(v1);
    v2v2 = v2.dot(v2);
    v1v2 = v1.dot(v2);

    float base = 0.5/(v1v1*v2v2-v1v2*v1v2);
    float k1 = base*v2v2*(v1v1-v1v2);
    float k2 = base*v1v1*(v2v2-v1v2);
    c = p1 + v1*k1 + v2*k2; // center

    radius = (c-p1).norm();
}

当你有中心时,用所需的步长在弧上生成点并不难(请注意,你不能制作“所有点” - 有无限数量)。

使用角度/距离步长生成点的简单方法是使用SLERP。首先获取向量

p0 = Start - Center
 and 
p1 = End - Center 

然后用acos和点积计算Omega(全弧角)

Omega = acos(p0.dot.p1 / R^2)

然后通过t 参数在0..1 范围内应用插值

slerp(t) = p0 * sin((1-t)*Omega) / sin(Omega) + p1 * sin(t*Omega) / sin(Omega)

【讨论】:

  • 非常感谢^^它有效,我已经坚持了好几天了,测试不同的公式。现在我可以回去工作了。
猜你喜欢
  • 2014-02-06
  • 1970-01-01
  • 2013-04-29
  • 2017-09-09
  • 1970-01-01
  • 2017-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多