场景:容器中存放液体,需要根据雷达测量出的液位计算出容器内的液体体积。
数据:扫描仪扫描容器生成STL文件。
效果图
处理关键代码:
数据结构
/// <summary>
/// 顶点数据结构
/// </summary>
public class Point3
{
public Point3()
{
}
public Point3(float x = 0, float y = 0, float z = 0)
{
X = x;
Y = y;
Z = z;
}
public float X { get; set; }
public float Y { get; set; }
public float Z { get; set; }
}
/// <summary>
/// 三角面数据结构
/// </summary>
public class Face
{
/// <summary>
/// 法矢量
/// </summary>
public Point3 Vector { get; set; }
public Point3 P1 { get; set; }
public Point3 P2 { get; set; }
public Point3 P3 { get; set; }
/// <summary>
/// 三角面片的属性信息
/// </summary>
public short Attribute { get; set; }
}
全局变量
/// <summary>
/// 三角面集合
/// </summary>
private List<Face> Faces { get; set; }
读取二进制STL文件
byte[] b = null;
try
{
DtStart = DateTime.Now;
using (var fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read))
{
var length = (int)fs.Length;
b = new byte[length];
fs.Read(b, 0, length);
fs.Close();
}
}
catch (Exception e)
{
b = null;
AddLog(e.Message);
MessageBox.Show(e.Message);
}
解析三角面数据
if (b == null) return;
var count = BitConverter.ToInt32(b, 80);
for (int i = 0; i < count; i++)
{
var vx = BitConverter.ToSingle(b, i * 50 + 84);
var vy = BitConverter.ToSingle(b, i * 50 + 88);
var vz = BitConverter.ToSingle(b, i * 50 + 92);
var x1 = BitConverter.ToSingle(b, i * 50 + 96);
var y1 = BitConverter.ToSingle(b, i * 50 + 100);
var z1 = BitConverter.ToSingle(b, i * 50 + 104);
var x2 = BitConverter.ToSingle(b, i * 50 + 108);
var y2 = BitConverter.ToSingle(b, i * 50 + 112);
var z2 = BitConverter.ToSingle(b, i * 50 + 116);
var x3 = BitConverter.ToSingle(b, i * 50 + 120);
var y3 = BitConverter.ToSingle(b, i * 50 + 124);
var z3 = BitConverter.ToSingle(b, i * 50 + 128);
var at = BitConverter.ToInt16(b, i * 50 + 132);
var face = new Face
{
Vector = new Point3(vx, vy, vz),
P1 = new Point3(x1, y1, z1),
P2 = new Point3(x2, y2, z2),
P3 = new Point3(x3, y3, z3),
Attribute = at
};
Faces.Add(face);
}
计算体积和面积
private void Calc()
{
var v = 0d;
var s = 0d;
foreach (var f in Faces)
{
v += TriangularVolume(
f.P1.X, f.P1.Y, f.P1.Z,
f.P2.X, f.P2.Y, f.P2.Z,
f.P3.X, f.P3.Y, f.P3.Z);
s += TriangularArea(
f.P1.X, f.P1.Y, f.P1.Z,
f.P2.X, f.P2.Y, f.P2.Z,
f.P3.X, f.P3.Y, f.P3.Z);
}
labelControl1.Text = $@"面积:{(s / 100):F3} cm2{Environment.NewLine}体积:{(v / 1000):F3} cm3";
}
/// <summary>
/// 计算体积
/// </summary>
private double TriangularVolume(float p1X, float p1Y, float p1Z,
float p2X, float p2Y, float p2Z,
float p3X, float p3Y, float p3Z)
{
double v321 = p3X * p2Y * p1Z;
double v231 = p2X * p3Y * p1Z;
double v312 = p3X * p1Y * p2Z;
double v132 = p1X * p3Y * p2Z;
double v213 = p2X * p1Y * p3Z;
double v123 = p1X * p2Y * p3Z;
return (double)(1.0 / 6.0) * (-v321 + v231 + v312 - v132 - v213 + v123);
}
/// <summary>
/// 计算面积
/// </summary>
private double TriangularArea(float p1X, float p1Y, float p1Z,
float p2X, float p2Y, float p2Z,
float p3X, float p3Y, float p3Z)
{
double ax = p2X - p1X;
double ay = p2Y - p1Y;
double az = p2Z - p1Z;
double bx = p3X - p1X;
double by = p3Y - p1Y;
double bz = p3Z - p1Z;
double cx = ay * bz - az * by;
double cy = az * bx - ax * bz;
double cz = ax * by - ay * bx;
return (double)(0.5 * Math.Sqrt(cx * cx + cy * cy + cz * cz));
}
对面数据排序
Faces.Sort(((a, b) => b.P1.Z.CompareTo(a.P1.Z)));
UpDown控制步进计算不同高度体积
try
{
if (Faces == null || Faces.Count == 0) return;
var t1 = DateTime.Now;
var spinValue = (float)spinEdit1.Value;
var v = 0d;
foreach (var f in Faces)
{
if (f.P1.Z <= spinValue)
{
v += TriangularVolume(
f.P1.X, f.P1.Y, f.P1.Z,
f.P2.X, f.P2.Y, f.P2.Z,
f.P3.X, f.P3.Y, f.P3.Z);
}
}
var t2 = (DateTime.Now - t1).TotalMilliseconds;
labelControl2.Text = $@"体积:{(v / 1000):F3} cm3 {Environment.NewLine}用时:{t2} ms";
}
catch (Exception exception)
{
Console.WriteLine(exception);
MessageBox.Show(exception.Message);
}
源码:
https://download.youkuaiyun.com/download/songlinok/70112610