使用 XYZ 坐标计算对象体积 [英] Calculate object volume with XYZ coordinates

查看:116
本文介绍了使用 XYZ 坐标计算对象体积的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

My Winforms app gets a CSV file with XYZ coordinates given from a 3D camera. With these coordinates I need to calculate the object's volume in cubic decimeters (dm3).

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

I am overwhelmed and not a math expert. I expected to be a library or an algorithm to already do that but the only things I found are in C++ like the PCL library or they use Unity. Is there a simple/clean way for a geometry ignorant guy like me to get the volume of an object with the XYZ coordinates?

更新

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

This is the fragment of the code I have this far:

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);
    }
}

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

Should the vectors array be ordered by proximity? How do I populate the Triangles property?

欢迎任何建议或指导.

推荐答案

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

This is how I did this using .STL files which arrange points into triangular faces. In your case, you need to somehow describe which points (nodes) combine to define faces, and to make sure the faces form a closed watertight solid.

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

The idea is the each three points ABC that form a face together with the origin form a solid of volume

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

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

It turns out that when you add up all the volumes, some will be positive (facing away from origin) and some negative (facing towards origin). In the end, the sum will equal the enclosed volume of the object.

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

Here is a sample of the C# code I am using to get solid object properties from a mesh. Remember a mesh is a collection of points called Nodes and a collection of triangles called Faces defined by the three index values of the points in the vertices.

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());
    }
}

<小时>

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


as a test case I used just one triangle defined as follows:

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

furthermore I verified the result with a more complex shape by comparing the above calculation to that produced by a commercial CAD package.

这篇关于使用 XYZ 坐标计算对象体积的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆