Вычисление площади неправильного многоугольника в C#
Я уже успел написать "для чайников" Как вычислить площадь неправильного многоугольника в C#но мне нужно, чтобы он был динамичным для любого количества вертикалей.
может кто-нибудь помочь?
класс:
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;
код для расчета при нажатии кнопки: (жестко закодированный для 4 точек полигона-должен быть для любой суммы...)
// 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}rn", 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}rn", 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}rn", 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}rn", 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}rn", x5, y5);
txtLog.Text += "rn";
// 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}rn", area);
пример вывод:
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
Area = 83.2566504099523
4 ответов
используя лямбда-выражения, это становится тривиальным!
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);
алгоритм объяснил здесь:
[этот метод добавляет] области трапеций, определенные отброшенными ребрами многоугольника к оси Х. Когда программа рассматривает нижний край многоугольника, расчет дает отрицательную область, поэтому пространство между многоугольником и ось вычитается, оставляя площадь многоугольника.
общая расчетная площадь отрицательно, если многоугольник ориентирован по часовой стрелке, [так] функция просто возвращает абсолютное значение.
этот метод дает странные результаты для не простых полигонов (где пересекаются ребра).
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);
}
что-то подобное обычный полигон (составитель Блокнота):
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;
}
хотя ваш подход не обращает внимания на ось Z. Поэтому я бы посоветовал применить некоторое преобразование, чтобы избавиться от него: вы не сможете получить область, если многоугольник не является плоскостью, тогда как если это плоскость, вы можете избавиться от третьего измерения.
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();