使用 XYZ 坐标计算对象体积

Dan*_*zos 1 .net c# 3d winforms

我的 Winforms 应用程序获取一个 CSV 文件,其中包含 3D 相机给出的 XYZ 坐标。使用这些坐标,我需要以立方分米 (dm3) 为单位计算物体的体积。

我不知所措,而不是数学专家。我希望成为一个已经这样做的库或算法,但我发现的唯一东西是在 C++ 中,如 PCL 库或它们使用 Unity。对于像我这样对几何学无知的人来说,是否有一种简单/干净的方法来获得具有 XYZ 坐标的对象的体积?

更新

这是我到目前为止的代码片段:

public class Volume
{
    //These are only part of the coordinates in the CSV file. There are more than 69.000 lines
    Vector3[] vectors = new Vector3[8]
        {
            new Vector3 {X=-139,Y=-109,Z=285},
            new Vector3 {X=-138,Y=-109,Z=286},
            new Vector3 {X=-136,Y=-109,Z=286},
            new Vector3 {X=-135,Y=-109,Z=286},
            new Vector3 {X=-133,Y=-109,Z=286},
            new Vector3 {X=-132,Y=-109,Z=286},
            new Vector3 {X=-130,Y=-109,Z=286},
            new Vector3 {X=-129,Y=-109,Z=286}
        };

    public double VolumeOfMesh()
    {
        Mesh _mesh = new Mesh();
        double volume = 0.0;

        _mesh.Vertices = vectors; //Should the vectors be organized by proximity to create the triangles?
        _mesh.Triangles = null; //How do I calculate the triangles?

        Vector3[] vertices = _mesh.Vertices;
        int[] triangles = _mesh.Triangles;

        for (int i = 0; i < _mesh.Triangles.Length; i += 3)
        {
            Vector3 p1 = vertices[triangles[i + 0]];
            Vector3 p2 = vertices[triangles[i + 1]];
            Vector3 p3 = vertices[triangles[i + 2]];

            volume += SignedVolumeOfTriangle(p1, p2, p3);
        }

        return Math.Abs(volume);
    }

    private double SignedVolumeOfTriangle(Vector3 p1, Vector3 p2, Vector3 p3)
    {
        var v321 = p3.X * p2.Y * p1.Z;
        var v231 = p2.X * p3.Y * p1.Z;
        var v312 = p3.X * p1.Y * p2.Z;
        var v132 = p1.X * p3.Y * p2.Z;
        var v213 = p2.X * p1.Y * p3.Z;
        var v123 = p1.X * p2.Y * p3.Z;
        return (1.0 / 6.0) * (-v321 + v231 + v312 - v132 - v213 + v123);
    }
}
Run Code Online (Sandbox Code Playgroud)

向量数组应该按接近度排序吗?如何填充 Triangles 属性?

欢迎任何建议或指导。

ja7*_*a72 7

这就是我使用 .STL 文件将点排列成三角形面的方式。在您的情况下,您需要以某种方式描述哪些点(节点)组合以定义面,并确保面形成封闭的水密实体。

楔

这个想法是形成一个面的每三个点ABC与原点一起形成一个体积实体

体积

其中 · 是向量点积,× 是向量叉积。

事实证明,当您将所有体积相加时,有些为正(背离原点),有些为负(面向原点)。最后,总和将等于物体的封闭体积。

这是C#我用来从网格获取实体对象属性的代码示例。请记住,网格是由顶点中点的三个索引值定义的Nodes称为点的集合和称为三角形的集合Faces

public struct Face3 
{
    public Face3(int indexA, int indexB, int indexC)
    {
        this.IndexA = indexA;
        this.IndexB = indexB;
        this.IndexC = indexC;
    }
    public readonly int IndexA, IndexB, IndexC;
}

public class Mesh3 
{
    public Mesh3(int n_nodes, int n_elements)
    {
        this.Nodes = new Vector3[n_nodes];
        this.Faces = new Face3[n_elements];
    }
    public Mesh3(Vector3[] nodes, Face3[] faces)
    {
        this.Nodes = nodes;
        this.Faces = faces;
    }

    public Vector3[] Nodes { get; }
    public Face3[] Faces { get; }

    public void CalcRigidBodyProperties(double density)
    {
        double sum_vol = 0;
        Vector3 sum_cg = Vector3.Zero;

        for (int i = 0; i < Faces.Length; i++)
        {
            var face = this.Faces[i];

            Vector3 a = this.Nodes[face.IndexA];
            Vector3 b = this.Nodes[face.IndexB];
            Vector3 c = this.Nodes[face.IndexC];

            double face_vol = Vector3.Dot(a, Vector3.Cross(b,c))/6;
            sum_vol += face_vol;

            Vector3 face_cg = (a+b+c)/4;

            sum_cg += frace_vol*face_cg;

        }
        // scale volume with density for mass
        var mass = density*sum_vol;
        // find center of mass by dividing by total volume
        var cg = sum_cg / sum_vol;
        ...
    }

    public static Mesh3 FromStl(string filename, double scale = 1)
    {
        // Imports a binary STL file
        // Code Taken From:
        // https://sukhbinder.wordpress.com/2013/12/10/new-fortran-stl-binary-file-reader/
        // Aug 27, 2019
        var fs = File.OpenRead(filename);
        var stl = new BinaryReader(fs);

        var header = new string(stl.ReadChars(80));
        var n_elems = stl.ReadInt32();

        var nodes = new List<Vector3>();
        var faces = new List<Face3>();

        bool FindIndexOf(Vector3 node, out int index)
        {
            for (index = 0; index < nodes.Count; index++)
            {
                if (nodes[index].Equals(node, TrigonometricPrecision))
                {
                    return true;
                }
            }
            index = -1;
            return false;
        }
        for (int i = 0; i < n_elems; i++)
        {
            var normal = new Vector3(
                stl.ReadSingle(),
                stl.ReadSingle(),
                stl.ReadSingle());
            var a = new Vector3(
                scale*stl.ReadSingle(),
                scale*stl.ReadSingle(),
                scale*stl.ReadSingle());
            var b = new Vector3(
                scale*stl.ReadSingle(),
                scale*stl.ReadSingle(),
                scale*stl.ReadSingle());
            var c = new Vector3(
                scale*stl.ReadSingle(),
                scale*stl.ReadSingle(),
                scale*stl.ReadSingle());
            // burn two bytes
            var temp = stl.ReadBytes(2);

            // get index of next point, and add point to list of nodes
            index_a = nodes.Count;
            nodes.Add(a);
            index_b = nodes.Count;
            nodes.Add(b);
            index_c = nodes.Count;
            nodes.Add(c);
            // add face from the three index values
            faces.Add(new Face3( index_a, index_b, index_c ));
        }

        stl.Close();

        return new Mesh3(nodes.ToArray(), faces.ToArray());
    }
}
Run Code Online (Sandbox Code Playgroud)

作为测试用例,我只使用了一个定义如下的三角形:

例子1 例子2

此外,我通过将上述计算与商业 CAD 软件包产生的计算进行比较,以更复杂的形状验证了结果。

例子3