2013-12-19 54 views
2

我试图计算位于平面上的多边形面积(一个集合共面点形成非相交封闭形状),并且我知道一个方法那可以计算出area of an irregular (or any) polygon in two dimensions - 但不是三个。我的解决方案是旋转平面,使其在z方向上正常为0(因此我可以将它视为2D),然后运行2D区域函数。计算平面上封闭多边形的面积

问题是我不知道如何实际确定旋转轴,并将其平面化为Z轴。我通过我可以找到的三维旋转最简单的方法进行旋转:Rotation Matrices。所以,鉴于我正在尝试使用旋转矩阵来进行旋转,我如何计算角度来旋转我的平面,使其与另一个矢量的方向相同?我实际上并不知道很多微积分或欧几里德几何,所以无论哪种解决方案都需要我至少教导我自己,这是理想的解决方案。有没有更好的办法?

下面是我的尝试,它甚至没有接近Z轴上的平面。这是我的“Surface”类的一个实例方法,它是我的“Plane”类的衍生物,并具有形成闭合多边形的共面点阵列(IntersectPoints)。

public virtual double GetArea() 
    { 
     Vector zUnit = new Vector(0, 0, 1); //vector perprendicualr to z 
     Vector nUnit = _normal.AsUnitVector(); 
     Surface tempSurface = null; 
     double result = 0; 

     if (nUnit != zUnit && zUnit.Dot(nUnit) != 0) //0 = perprendicular to z 
     { 
      tempSurface = (Surface)Clone(); 
      double xAxisAngle = Vector.GetAxisAngle(nUnit, zUnit, Physics.Formulae.Axes.X); 
      double yAxisAngle = Vector.GetAxisAngle(nUnit, zUnit, Physics.Formulae.Axes.Y); 
      double rotationAngle = Vector.GetAxisAngle(nUnit, zUnit, Physics.Formulae.Axes.Z); 
      tempSurface.Rotate(xAxisAngle, yAxisAngle, rotationAngle); //rotating plane so that it is flat on the Z axis 
     } 
     else 
     { 
      tempSurface = this; 
     } 

     for (int x = 0; x < tempSurface.IntersectPoints.Count; x++) //doing a cross sum of each point 
     { 
      Point curPoint = tempSurface.IntersectPoints[x]; 
      Point nextPoint; 

      if (x == tempSurface.IntersectPoints.Count - 1) 
      { 
       nextPoint = tempSurface.IntersectPoints[0]; 
      } 
      else 
      { 
       nextPoint = tempSurface.IntersectPoints[x + 1]; 
      } 

      double cross1 = curPoint.X * nextPoint.Y; 
      double cross2 = curPoint.Y * nextPoint.X; 
      result += (cross1 - cross2); //add the cross sum of each set of points to the result 
     } 

     return Math.Abs(result/2); //divide cross sum by 2 and take its absolute value to get the area. 
    } 

这里是我的核心轮换和获得轴角方法:

private Vector Rotate(double degrees, int axis) 
    { 
     if (degrees <= 0) return this; 
     if (axis < 0 || axis > 2) return this; 

     degrees = degrees * (Math.PI/180); //convert to radians 
     double sin = Math.Sin(degrees); 
     double cos = Math.Cos(degrees); 
     double[][] matrix = new double[3][]; 

     //normalizing really small numbers to actually be zero 
     if (Math.Abs(sin) < 0.00000001) 
     { 
      sin = 0; 
     } 
     if (Math.Abs(cos) < 0.0000001) 
     { 
      cos = 0; 
     } 

     //getting our rotation matrix 
     switch (axis) 
     { 
      case 0: //x axis 
       matrix = new double[][] 
       { 
        new double[] {1, 0, 0}, 
        new double[] {0, cos, sin * -1}, 
        new double[] {0, sin, cos} 
       }; 
       break; 
      case 1: //y axis 
       matrix = new double[][] 
       { 
        new double[] {cos, 0, sin}, 
        new double[] {0, 1, 0}, 
        new double[] {sin * -1, 0, cos} 
       }; 
       break; 
      case 2: //z axis 
       matrix = new double[][] 
       { 
        new double[] {cos, sin * -1, 0}, 
        new double[] {sin, cos, 0}, 
        new double[] {0, 0, 1} 
       }; 
       break; 
      default: 
       return this; 
     } 

     return Physics.Formulae.Matrix.MatrixByVector(this, matrix); 
    } 

public static double GetAxisAngle(Point a, Point b, Axes axis, bool inDegrees = true) 
    { //pretty sure this doesnt actually work 
     double distance = GetDistance(a, b); 
     double difference; 

     switch (axis) 
     { 
      case Axes.X: 
       difference = b.X - a.X; 
       break; 
      case Axes.Y: 
       difference = b.Y - a.Y; 
       break; 
      case Axes.Z : 
       difference = b.Z - a.Z; 
       break; 
      default: 
       difference = 0; 
       break; 
     } 

     double result = Math.Acos(difference/distance); 

     if (inDegrees == true) 
     { 
      return result * 57.2957; //57.2957 degrees = 1 radian 
     } 
     else 
     { 
      return result; 
     } 
    } 
+0

你需要对3个独立的轴旋转,以达到你所期望的状态?另外,你可以用一个新的坐标系来表示三维形状吗(即:改变为正交基准)? – Warty

+0

我相信我需要旋转三个独立的轴。至于你的第二个问题,我不确定。目前,我不能。我当然可以写一个算法来表达另一种方式,如果这样会更容易旋转。 – Richard

+0

如果你可以改变你的观点以便选择正确的基准,你可以使用由你的多边形中的任何非共线3点p0,p1,p2形成的基础。新的向量空间将包含由p0 + a * basisVec0 + b * basisVec1定义的点,并且您的坐标将为[a,b]。既然你的基础是正交的,你可以用这些[a,b]作为插入你的2D函数的点。另外,如果您选择沿着这条路线走下去,我坚信您可以实现与xy平面共面的方向(从而与z轴垂直)两次旋转。 – Warty

回答

0

你不需要旋转平面(或所有点)。只需计算多边形投影到Z平面的面积(如果它不垂直于多边形平面),例如,使用GetArea函数,并将结果除以多边平面的余弦-Z平面角度 - 它等于标量zUnit和NUnit的产物(我建议NUnit的是法向矢量多边形平面)

TrueArea = GetArea()/zUnit.Dot(nUnit) 
0

的鲁棒的方式来做到这一点是做每个边缘的顶点的叉积的总和。如果顶点是共面的,这将产生一个法线,该法线的长度是闭合多边形面积的2倍。

注意,此方法非常相似,在你的问题,这实际上计算2D相当于3D跨产品,总结所有边链接2D法,然后除以2

Vector normal = points[count-1].cross(points[0]); 
for(int i=1; i<count; ++i) { 
    normal += points[i-1].cross(points[i]); 
} 
double area = normal.length() * 0.5; 

这种方法的优点:

  • 如果你的顶点是唯一平面,它仍然给出了正确的答案
  • 它不依赖于的角度平面。
  • 事实上,你根本不需要处理角度。
  • 如果你知道飞机的方向,你已经正常了。

一个可能的困难:如果你的多边形非常小,并且离原点很远,你可以得到浮点精度问题。如果这种情况下,很可能会出现,你应该首先把所有的顶点,这样一个在原点,就像这样:

Vector normal(0,0,0); 
Vector origin = points[count-1]; 
for(int i=1; i<count-1; ++i) { 
    normal += (points[i-1]-origin).cross(points[i]-origin); 
} 
double area = normal.length() * 0.5;