using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.IO;
using System.Drawing;
namespace TIN_321
{
public partial class Form1 : Form
{
string[] all_lines;
List<Mypoints> allPoints = new List<Mypoints>();
string jzH;
// 初始三角网构建
List<Triangle> T1 = new List<Triangle>();
List<Triangle> T2 = new List<Triangle>();
List<Edge> S = new List<Edge>();
public Form1()
{
InitializeComponent();
}
private bool isDragging = false;
private Point mouseDownLocation;
private void 打开文件ToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog op = new OpenFileDialog();
if (op.ShowDialog() == DialogResult.OK)
{
all_lines = File.ReadAllLines(op.FileName);
}
else
{
return;
}
try
{
jzH = all_lines[0].Split(',')[1];
for (int i = 2; i < all_lines.Length; i++)
{
string[] part = all_lines[i].Split(',');
Mypoints p = new Mypoints
{
name = part[0],
x = double.Parse(part[1]),
y = double.Parse(part[2]),
h = double.Parse(part[3])
};
allPoints.Add(p);
dataGridView1.Rows.Add(part[0], part[1], part[2], part[3]);
}
toolStripStatusLabel1.Text = "导入成功!";
toolStripStatusLabel2.Text = "基准高程:" + jzH;
}
catch
{
MessageBox.Show("导入失败");
return;
}
}
private void 计算三角网及体积ToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show("正在计算,请稍后");
if (allPoints.Count < 3)
{
MessageBox.Show("至少需要三个点才能计算凸包!");
return;
}
// 找到最下方的点,作为基点
Mypoints P0 = allPoints.OrderBy(p => p.y).ThenBy(p => p.x).First();
// 排序其他点
List<Mypoints> sortedPoints = allPoints.Where(p => p != P0).ToList();
sortedPoints.Sort((p1, p2) =>
{
double angle1 = Math.Atan2(p1.y - P0.y, p1.x - P0.x);
double angle2 = Math.Atan2(p2.y - P0.y, p2.x - P0.x);
if (angle1 == angle2)
{
double distance1 = Distance(P0, p1);
double distance2 = Distance(P0, p2);
return distance2.CompareTo(distance1); // 保证按距离从远到近排列
}
return angle1.CompareTo(angle2); // 按角度排序
});
List<Mypoints> convexHull = new List<Mypoints> { P0, sortedPoints[0], sortedPoints[1] };
foreach (var p in sortedPoints.Skip(2))
{
while (convexHull.Count >= 2 && CrossProduct(convexHull[convexHull.Count - 2], convexHull[convexHull.Count - 1], p) <= 0)
{
convexHull.RemoveAt(convexHull.Count - 1);
}
convexHull.Add(p);
}
// 构建初始三角形
List<Mypoints> innerPoints = allPoints.Where(p => !convexHull.Contains(p)).ToList();
foreach (var p in innerPoints)
{
foreach (var edge in convexHull.Select((point, index) => new { point, index }))
{
var triangle = new Triangle(p, edge.point, convexHull[(edge.index + 1) % convexHull.Count]);
T1.Add(triangle);
}
}
// 生成三角网并移除无效三角形
foreach (var p in innerPoints)
{
List<Triangle> tempT1 = new List<Triangle>(T1);
foreach (var triangle in tempT1)
{
double[] circumcenter = CalculateCircumcenter(triangle);
double x0 = circumcenter[0];
double y0 = circumcenter[1];
double r = circumcenter[2];
if (IsInsideCircumcircle(p, x0, y0, r))
{
T1.Remove(triangle);
T2.Add(triangle);
}
}
UpdateEdgesAndTriangles(T2, S, p);
}
RemoveSmallOrLargeTriangles(T1);
toolStripStatusLabel1.Text = "不规则三角网生成完成!";
OutputFirstTwentyTriangles();
richTextBox1.Clear();
double balanceElevation = CalculateBalanceElevation(T1);
richTextBox1.AppendText($"平衡高程: {balanceElevation}\n");
double referenceHeight = double.Parse(jzH);
CalculateCutFillVolume(T1, referenceHeight);
DrawConvexHullPoints(convexHull);
DrawTrianglesOnChart(T1);
MessageBox.Show("完成!");
}
private void DrawConvexHullPoints(List<Mypoints> convexHull)
{
chart2.Series.Clear();
var convexHullSeries = new System.Windows.Forms.DataVisualization.Charting.Series
{
Name = "ConvexHullPoints",
ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Point,
MarkerStyle = System.Windows.Forms.DataVisualization.Charting.MarkerStyle.Circle,
MarkerSize = 10,
BorderWidth = 3,
Color = System.Drawing.Color.Red
};
foreach (var point in convexHull)
{
convexHullSeries.Points.AddXY(point.x, point.y);
}
chart2.Series.Add(convexHullSeries);
chart2.ChartAreas[0].AxisY.Minimum = 2800;
chart2.ChartAreas[0].AxisY.Maximum = 3000;
}
private void DrawTrianglesOnChart(List<Triangle> triangles)
{
chart1.Series.Clear();
var series = new System.Windows.Forms.DataVisualization.Charting.Series
{
Name = "Triangles",
ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line,
BorderWidth = 2
};
foreach (var triangle in triangles)
{
AddTriangleEdgesToChart(triangle, series);
}
chart1.Series.Add(series);
}
private void AddTriangleEdgesToChart(Triangle triangle, System.Windows.Forms.DataVisualization.Charting.Series series)
{
AddEdgeToChart(triangle.A, triangle.B, series);
AddEdgeToChart(triangle.B, triangle.C, series);
AddEdgeToChart(triangle.C, triangle.A, series);
}
private void AddEdgeToChart(Mypoints p1, Mypoints p2, System.Windows.Forms.DataVisualization.Charting.Series series)
{
series.Points.AddXY(p1.x, p1.y); // 起点
series.Points.AddXY(p2.x, p2.y); // 终点
}
private double CalculateBalanceElevation(List<Triangle> triangles)
{
double numerator = 0;
double denominator = 0;
foreach (var triangle in triangles)
{
double avgHeight = (triangle.A.h + triangle.B.h + triangle.C.h) / 3;
double area = CalculateTriangleArea(triangle);
numerator += avgHeight * area;
denominator += area;
}
return numerator / denominator;
}
private double CalculateTriangleArea(Triangle triangle)
{
double x1 = triangle.A.x, y1 = triangle.A.y;
double x2 = triangle.B.x, y2 = triangle.B.y;
double x3 = triangle.C.x, y3 = triangle.C.y;
return Math.Abs((x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1)) / 2.0;
}
private void CalculateCutFillVolume(List<Triangle> triangles, double referenceHeight)
{
foreach (var triangle in triangles)
{
double avgHeight = (triangle.A.h + triangle.B.h + triangle.C.h) / 3;
double area = CalculateTriangleArea(triangle);
double deltaHeight = avgHeight - referenceHeight;
if (deltaHeight > 0)
{
// 填方
double fillVolume = area * deltaHeight;
richTextBox1.Text+=($"三角形: {triangle.A.name}, {triangle.B.name}, {triangle.C.name} 填方体积: {fillVolume}\n");
}
else
{
// 挖方
double cutVolume = area * (-deltaHeight);
richTextBox1.Text += ($"三角形: {triangle.A.name}, {triangle.B.name}, {triangle.C.name} 挖方体积: {cutVolume}\n");
}
}
}
private void OutputFirstTwentyTriangles()
{
var trianglesToDisplay = T1.Take(20).ToList(); // 获取前20个三角形
foreach (var triangle in trianglesToDisplay)
{
string triangleText = $"三角形: {triangle.A.name}, {triangle.B.name}, {triangle.C.name}\n";
richTextBox1.Text+=triangleText;
}
richTextBox1.Text += "前20个三角形已显示";
}
private double[] CalculateCircumcenter(Triangle triangle)
{
double x1 = triangle.A.x, y1 = triangle.A.y;
double x2 = triangle.B.x, y2 = triangle.B.y;
double x3 = triangle.C.x, y3 = triangle.C.y;
double d = 2 * (x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2));
double x0 = ((Math.Pow(x1, 2) + Math.Pow(y1, 2)) * (y2 - y3) + (Math.Pow(x2, 2) + Math.Pow(y2, 2)) * (y3 - y1) + (Math.Pow(x3, 2) + Math.Pow(y3, 2)) * (y1 - y2)) / d;
double y0 = ((Math.Pow(x1, 2) + Math.Pow(y1, 2)) * (x3 - x2) + (Math.Pow(x2, 2) + Math.Pow(y2, 2)) * (x1 - x3) + (Math.Pow(x3, 2) + Math.Pow(y3, 2)) * (x2 - x1)) / d;
double r = Math.Sqrt(Math.Pow(x0 - x1, 2) + Math.Pow(y0 - y1, 2));
return new double[] { x0, y0, r };
}
private double Distance(Mypoints p1, Mypoints p2)
{
return Math.Sqrt(Math.Pow(p2.x - p1.x, 2) + Math.Pow(p2.y - p1.y, 2));
}
private double CrossProduct(Mypoints p1, Mypoints p2, Mypoints p3)
{
return (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
}
private bool IsInsideCircumcircle(Mypoints p, double x0, double y0, double r)
{
return Math.Pow(p.x - x0, 2) + Math.Pow(p.y - y0, 2) < Math.Pow(r, 2);
}
private void UpdateEdgesAndTriangles(List<Triangle> T2, List<Edge> S, Mypoints p)
{
foreach (var triangle in T2)
{
AddEdge(triangle, S);
}
T2.Clear();
foreach (var edge in S)
{
var newTriangle = new Triangle(edge.P1, edge.P2, p);
T1.Add(newTriangle);
}
S.Clear();
}
private void AddEdge(Triangle triangle, List<Edge> edgeList)
{
AddEdgeToList(triangle.A, triangle.B, edgeList);
AddEdgeToList(triangle.B, triangle.C, edgeList);
AddEdgeToList(triangle.C, triangle.A, edgeList);
}
private void AddEdgeToList(Mypoints p1, Mypoints p2, List<Edge> edgeList)
{
if (!edgeList.Any(e => (e.P1 == p1 && e.P2 == p2) || (e.P1 == p2 && e.P2 == p1)))
{
edgeList.Add(new Edge(p1, p2));
}
}
private void RemoveSmallOrLargeTriangles(List<Triangle> triangles)
{
triangles.RemoveAll(t =>
{
double angleA = CalculateAngle(t.A, t.B, t.C);
double angleB = CalculateAngle(t.B, t.A, t.C);
double angleC = CalculateAngle(t.C, t.A, t.B);
return angleA < 5 || angleB < 5 || angleC < 5 || angleA > 160 || angleB > 160 || angleC > 160;
});
}
private double CalculateAngle(Mypoints A, Mypoints B, Mypoints C)
{
double ab = Math.Sqrt(Math.Pow(A.x - B.x, 2) + Math.Pow(A.y - B.y, 2));
double bc = Math.Sqrt(Math.Pow(B.x - C.x, 2) + Math.Pow(B.y - C.y, 2));
double ac = Math.Sqrt(Math.Pow(A.x - C.x, 2) + Math.Pow(A.y - C.y, 2));
return Math.Acos((ab * ab + bc * bc - ac * ac) / (2 * ab * bc)) * (180 / Math.PI);
}
private void toolStripButton4_Click(object sender, EventArgs e)
{
double xMin = chart1.ChartAreas[0].AxisX.Minimum;
double xMax = chart1.ChartAreas[0].AxisX.Maximum;
double yMin = chart1.ChartAreas[0].AxisY.Minimum;
double yMax = chart1.ChartAreas[0].AxisY.Maximum;
chart1.ChartAreas[0].AxisX.Minimum = xMin + (xMax - xMin) * 0.1;
chart1.ChartAreas[0].AxisX.Maximum = xMax - (xMax - xMin) * 0.1;
chart1.ChartAreas[0].AxisY.Minimum = yMin + (yMax - yMin) * 0.1;
chart1.ChartAreas[0].AxisY.Maximum = yMax - (yMax - yMin) * 0.1;
}
private void toolStripButton5_Click(object sender, EventArgs e)
{
double xMin = chart1.ChartAreas[0].AxisX.Minimum;
double xMax = chart1.ChartAreas[0].AxisX.Maximum;
double yMin = chart1.ChartAreas[0].AxisY.Minimum;
double yMax = chart1.ChartAreas[0].AxisY.Maximum;
chart1.ChartAreas[0].AxisX.Minimum = xMin - (xMax - xMin) * 0.1;
chart1.ChartAreas[0].AxisX.Maximum = xMax + (xMax - xMin) * 0.1;
chart1.ChartAreas[0].AxisY.Minimum = yMin - (yMax - yMin) * 0.1;
chart1.ChartAreas[0].AxisY.Maximum = yMax + (yMax - yMin) * 0.1;
}
private void chart1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
isDragging = true;
mouseDownLocation = e.Location;
}
}
private void chart1_MouseMove(object sender, MouseEventArgs e)
{
if (isDragging)
{
// 计算鼠标移动的偏移量
int deltaX = e.X - mouseDownLocation.X;
int deltaY = e.Y - mouseDownLocation.Y;
// 获取当前X轴和Y轴的最小最大值
double xMin = chart1.ChartAreas[0].AxisX.Minimum;
double xMax = chart1.ChartAreas[0].AxisX.Maximum;
double yMin = chart1.ChartAreas[0].AxisY.Minimum;
double yMax = chart1.ChartAreas[0].AxisY.Maximum;
// 更新坐标轴的最小最大值来实现平移
chart1.ChartAreas[0].AxisX.Minimum = xMin - deltaX * (xMax - xMin) / chart1.Width;
chart1.ChartAreas[0].AxisX.Maximum = xMax - deltaX * (xMax - xMin) / chart1.Width;
chart1.ChartAreas[0].AxisY.Minimum = yMin + deltaY * (yMax - yMin) / chart1.Height;
chart1.ChartAreas[0].AxisY.Maximum = yMax + deltaY * (yMax - yMin) / chart1.Height;
// 更新鼠标按下的位置
mouseDownLocation = e.Location;
}
}
private void chart1_MouseUp(object sender, MouseEventArgs e)
{
isDragging = false; // 结束拖动
}
}
}
namespace TIN_321
{
public class Triangle
{
public Mypoints A { get; set; }
public Mypoints B { get; set; }
public Mypoints C { get; set; }
public Triangle(Mypoints a, Mypoints b, Mypoints c)
{
A = a;
B = b;
C = c;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TIN_321
{
public class Edge
{
public Mypoints P1 { get; set; }
public Mypoints P2 { get; set; }
public Edge(Mypoints p1, Mypoints p2)
{
P1 = p1;
P2 = p2;
}
}
}
TIN体积
于 2025-03-22 16:19:10 首次发布