2013-12-19 52 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;