2010-01-09 92 views
5

He logrado escribir un 'para principiantes' cómo calcular el área de polígono irregular en C#, pero necesito que sea dinámico para cualquier cantidad de verticies.Cálculo de área de polígono irregular en C#

¿Alguien puede ayudarnos?

Clase:

public class Vertex 
{ 
    private int _vertexIdx; 
    private double _coordX; 
    private double _coordY; 
    private double _coordZ; 

    public Vertex() 
    { } 

    public Vertex(int vertexIdx, double coordX, double coordY, double coordZ) 
    { 
     _vertexIdx = vertexIdx; 
     _coordX = coordX; 
     _coordY = coordY; 
     _coordZ = coordZ; 
    } 

    public int VertexIdx 
    { 
     get { return _vertexIdx; } 
     set { _vertexIdx = value; } 
    } 

    public double X 
    { 
     get { return _coordX; } 
     set { _coordX = value; } 
    } 

    public double Y 
    { 
     get { return _coordY; } 
     set { _coordY = value; } 
    } 

    public double Z 
    { 
     get { return _coordZ; } 
     set { _coordZ = value; } 
    } 
} 

Form_Load:

List<Vertex> verticies = new List<Vertex>(); 

verticies.Add(new Vertex(1, 930.9729, 802.8789, 0)); 
verticies.Add(new Vertex(2, 941.5341, 805.662, 0)); 
verticies.Add(new Vertex(3, 946.5828, 799.271, 0)); 
verticies.Add(new Vertex(4, 932.6215, 797.0548, 0)); 

dataGridView1.DataSource = verticies;

Código de calcular cuando se pulsa el botón: (no modificable por 4 puntos polígono - debe ser por cualquier cantidad. ..)

 // X-coords 
     double x1; 
     double x2; 
     double x3; 
     double x4; 
     double x5; 

     // Y-coords 
     double y1; 
     double y2; 
     double y3; 
     double y4; 
     double y5; 

     // Xn * Yn++ 
     double x1y2; 
     double x2y3; 
     double x3y4; 
     double x4y5; 

     // Yn * Xn++ 
     double y1x2; 
     double y2x3; 
     double y3x4; 
     double y4x5; 

     // XnYn++ - YnXn++ 
     double x1y2my1x2; 
     double x2y3my2x3; 
     double x3y4my3x4; 
     double x4y5my4x5; 

     double result; 
     double area; 

     x1 = Convert.ToDouble(dataGridView1.Rows[0].Cells[1].Value.ToString()); 
     y1 = Convert.ToDouble(dataGridView1.Rows[0].Cells[2].Value.ToString()); 
     txtLog.Text += String.Format("X1 = {0}\tY1 = {1}\r\n", x1, y1); 

     x2 = Convert.ToDouble(dataGridView1.Rows[1].Cells[1].Value.ToString()); 
     y2 = Convert.ToDouble(dataGridView1.Rows[1].Cells[2].Value.ToString()); 
     txtLog.Text += String.Format("X2 = {0}\tY2 = {1}\r\n", x2, y2); 

     x3 = Convert.ToDouble(dataGridView1.Rows[2].Cells[1].Value.ToString()); 
     y3 = Convert.ToDouble(dataGridView1.Rows[2].Cells[2].Value.ToString()); 
     txtLog.Text += String.Format("X3 = {0}\tY3 = {1}\r\n", x3, y3); 

     x4 = Convert.ToDouble(dataGridView1.Rows[3].Cells[1].Value.ToString()); 
     y4 = Convert.ToDouble(dataGridView1.Rows[3].Cells[2].Value.ToString()); 
     txtLog.Text += String.Format("X4 = {0}\tY4 = {1}\r\n", x4, y4); 

     // add the start point again 
     x5 = Convert.ToDouble(dataGridView1.Rows[0].Cells[1].Value.ToString()); 
     y5 = Convert.ToDouble(dataGridView1.Rows[0].Cells[2].Value.ToString()); 
     txtLog.Text += String.Format("X5 = {0}\tY5 = {1}\r\n", x5, y5); 
     txtLog.Text += "\r\n"; 

     // Multiply 
     x1y2 = x1 * y2; 
     x2y3 = x2 * y3; 
     x3y4 = x3 * y4; 
     x4y5 = x4 * y5; 

     y1x2 = y1 * x2; 
     y2x3 = y2 * x3; 
     y3x4 = y3 * x4; 
     y4x5 = y4 * x5; 

     // Subtract from each other 
     x1y2my1x2 = x1y2 - y1x2; 
     x2y3my2x3 = x2y3 - y2x3; 
     x3y4my3x4 = x3y4 - y3x4; 
     x4y5my4x5 = x4y5 - y4x5; 

     // Sum all results 
     result = x1y2my1x2 + x2y3my2x3 + x3y4my3x4 + x4y5my4x5; 
     area = Math.Abs(result/2); 

     txtLog.Text += String.Format("Area = {0}\r\n", area); 
salida

Ejemplo:

X1 = 930,9729 Y1 = 802,8789

X2 = 941,5341 Y2 = 805.662

X3 = 946,5828 Y3 = 799.271

X4 = 932,6215 Y4 = 797,0548

X5 = 930.9729 Y5 = 802.8789

Área = 83,2566504099523

+0

Captura de pantalla del cálculo de Microstation = http://img15.imageshack.us/img15/6523/areaofpolygon.png – riaandelange

+0

Un método típico que he visto antes es dividir el polígono en triángulos, entonces simplemente podría sumar el área de todos los triángulos. Sin embargo, esto no es trivial ya que necesita diferentes algoritmos dependiendo de la complejidad de los polígonos (cruces, agujeros, convexos/cóncavos, etc.) –

+0

Puede considerar hacer esta pregunta en http://mathoverflow.net/, un desbordamiento de pila como el sitio, solo para preguntas de matemáticas, solo asegúrate de plantear la pregunta como no programática y, en cambio, solicita el enfoque algorítmico. –

Respuesta

10

El uso de expresiones lambda esto se convierte en trivial!

var points = GetSomePoints(); 

points.Add(points[0]); 
var area = Math.Abs(points.Take(points.Count - 1) 
    .Select((p, i) => (points[i + 1].X - p.X) * (points[i + 1].Y + p.Y)) 
    .Sum()/2); 

El algoritmo se explica here.

+0

La "explicación" vinculada no * explica * el algoritmo. Simplemente presenta el código de manera diferente. –

3

Algo así por un plain polygon (compilado por el bloc de notas):

static double GetDeterminant(double x1, double y1, double x2, double y2) 
{ 
    return x1 * y2 - x2 * y1; 
} 

static double GetArea(IList<Vertex> vertices) 
{ 
    if(vertices.Count < 3) 
    { 
     return 0; 
    } 
    double area = GetDeterminant(vertices[vertices.Count - 1].X, vertices[vertices.Count - 1].Y, vertices[0].X, vertices[0].Y); 
    for (int i = 1; i < vertices.Count; i++) 
    { 
     area += GetDeterminant(vertices[i - 1].X, vertices[i - 1].Y, vertices[i].X, vertices[i].Y); 
    } 
    return area/2; 
} 

pesar de que su enfoque no presta atención al eje Z. Por lo tanto, recomendaría aplicar alguna transformación para deshacerse de ella: no podrá obtener área si el polígono no es plano, mientras que si es plano podrá deshacerse de la tercera dimensión.

+0

entonces, ¿hay una diferencia entre el área calculada en 2D y 3D? – riaandelange

+0

Sí, solo un poco. El área se puede calcular SOLO para objetos simples. Por lo tanto, su polígono DEBE ser simple: todos sus vértices deben estar en la misma llanura, de lo contrario el área no se puede calcular. El problema es que este avión no siempre será Z = 0 plano como en tu ejemplo. Debe tener esto en cuenta y procesar los puntos de manera apropiada antes de calcular el área. – Li0liQ

0
 double resultant = 0; 
     double area = 0; 
     int tel1 = 0; 
     int tel2 = 0; 

     x1y2lst.Clear(); 
     y1x2lst.Clear(); 
     x1y2lstMinusy1x2lst.Clear(); 

     // *******************************************************************************************// 
     // Calculate and populate X1 * Y2 in a list 

     for (int i = 0; i < dataGridView1.Rows.Count - 1; i++) 
     { 
      tel1++; 
      double x1x = Convert.ToDouble(dataGridView1.Rows[i].Cells[1].Value.ToString()); 
      double y2y = Convert.ToDouble(dataGridView1.Rows[i+1].Cells[2].Value.ToString()); 
      x1y2lst.Add(x1x * y2y); 
     } 
     // Calculate the last with the first value 
     double xLastx = Convert.ToDouble(dataGridView1.Rows[tel1].Cells[1].Value.ToString()); 
     double yFirsty = Convert.ToDouble(dataGridView1.Rows[0].Cells[2].Value.ToString()); 
     x1y2lst.Add(xLastx * yFirsty); 

     // *******************************************************************************************// 
     // Calculate and populate Y1 * X2 in a list 
     for (int i = 0; i < dataGridView1.Rows.Count - 1; i++) 
     { 
      tel2++; 
      double y1y = Convert.ToDouble(dataGridView1.Rows[i].Cells[2].Value.ToString()); 
      double x2x = Convert.ToDouble(dataGridView1.Rows[i + 1].Cells[1].Value.ToString()); 
      y1x2lst.Add(y1y * x2x); 
     } 
     // Calculate the last with the first value 
     double yLasty = Convert.ToDouble(dataGridView1.Rows[tel2].Cells[2].Value.ToString()); 
     double xFirstx = Convert.ToDouble(dataGridView1.Rows[0].Cells[1].Value.ToString()); 
     y1x2lst.Add(yLasty * xFirstx); 

     // Subract List1 values from List2 values 
     for (int k = 0; k < x1y2lst.Count; k++) 
     { 
      x1y2lstMinusy1x2lst.Add(x1y2lst[k] - y1x2lst[k]); 
     } 

     // Add all answers from previous to a result 
     for (int l = 0; l < x1y2lstMinusy1x2lst.Count; l++) 
     { 
      resultant += x1y2lstMinusy1x2lst[l]; 
     } 
     // Area = Result from steps above devided by 2 
     area = Math.Abs(resultant/2); 
     txtArea.Text = Math.Round(area, 4).ToString(); 
4
public float Area(List<PointF> vertices) 
{ 
    vertices.Add(vertices[0]); 
    return Math.Abs(vertices.Take(vertices.Count - 1).Select((p, i) => (p.X * vertices[i + 1].Y) - (p.Y * vertices[i + 1].X)).Sum()/2); 
} 
+1

Proporcione alguna explicación para que su respuesta sea más fácil de entender. – Carsten

Cuestiones relacionadas