gpt4 book ai didi

C# 如何解析 STL 文件。当前函数没有正确地将顶点链接到面中。算法错误

转载 作者:行者123 更新时间:2023-12-05 03:39:30 25 4
gpt4 key购买 nike

我正在开发一个需要在 OpenGL 中显示 STL 文件的 C# 项目。为此,我创建了一个返回自定义 Part 对象的 STL 解析类。我有解析类,我可以在其中导入 ASCII 和二进制文件。到目前为止,ASCII 导入似乎工作正常。这是我从维基百科获得的 ASCII 文件的线框示例: Wireframe Image of a Sphereicon Link to Model

橙色线是表面边缘,青色线是表面法线(为了便于查看而缩短)。这告诉我我的显示代码、ASCII 导入器和 Part 类都没有问题。但是,二进制导入器出现了一个奇怪的问题。这是导入 Blender 的球体 STL 对象的图像: Sphere in Blender这是同一球体在我的 OpenGL 软件中的渲染图: My render of the same sphere

表面以一种奇怪的方式在它们之间划分了顶点。每个四边形的中心似乎还有一个额外的顶点。

我正在关注 STL Format维基百科上的规范。

导入二进制()

/** importBinary attempts to import the specified Binary STL file and returns a Part object.
*
* This function is responsible for reading and decoding Binary style STL files.
*
* @Param fileName The path to the STL file to import
*/
private static Part importBinary(string fileName)
{
Part retPart = new Part(fileName);
try
{
// In order to read a binary format, a BinaryReader must be used. BinaryReader itself
// is not thread safe. To make it so, a locker object and lock() must be used.
Object locker = new Object();
lock (locker)
{
using (BinaryReader br = new BinaryReader(File.Open(fileName, FileMode.Open)))
{
// Read header info
byte[] header = br.ReadBytes(80);
byte[] length = br.ReadBytes(4);
int numberOfSurfaces = BitConverter.ToInt32(length,0);
string headerInfo = Encoding.UTF8.GetString(header, 0, header.Length).Trim();
Console.WriteLine(String.Format("\nImporting: {0}\n\tHeader: {1}\n\tNumber of faces:{2}\n", fileName, headerInfo, numberOfSurfaces));

// Read Data
byte[] block;
int surfCount = 0;

// Read from the file until either there is no data left or
// the number of surfaces read is equal to the number of surfaces in the
// file. This can prevent reading a partial block at the end and getting
// out of range execptions.
while ((block = br.ReadBytes(50)) != null && surfCount++ < numberOfSurfaces)
{
// Declare temp containers
Surface newSurf = new Surface();
List<Vector3d> verts = new List<Vector3d>();
byte[] xComp = new byte[4];
byte[] yComp = new byte[4];
byte[] zComp = new byte[4];

// Parse data block
for (int i = 0; i < 4; i++)
{
for (int k = 0; k < 12; k++)
{
int index = k + i * 12;

if (k < 4)
{
// xComp
xComp[k] = block[index];
}
else if (k < 8)
{
// yComp
yComp[k - 4] = block[index];
}
else
{
// zComp
zComp[k - 8] = block[index];
}
}
// Convert data to useable structures
float xCompReal = BitConverter.ToSingle(xComp, 0);
float yCompReal = BitConverter.ToSingle(yComp, 0);
float zCompReal = BitConverter.ToSingle(zComp, 0);

if (i == 1)
{
// This is a normal
Vector3d norm = new Vector3d();
norm.X = xCompReal;// * scaleFactor;
norm.Y = yCompReal;// * scaleFactor;
norm.Z = zCompReal;// * scaleFactor;
//if(Math.Abs(Math.Pow(norm.X,2) + Math.Pow(norm.X, 2) + Math.Pow(norm.X, 2) - 1) > .001)
//{
// Console.WriteLine("ERROR: Improper file read. Surface normal is not a unit vector.");
//}
newSurf.normal = norm;
}
else
{
// This is a vertex
Vector3d vert = new Vector3d();
vert.X = xCompReal * scaleFactor;
vert.Y = yCompReal * scaleFactor;
vert.Z = zCompReal * scaleFactor;
verts.Add(vert);
}
}
newSurf.vertices = verts;
retPart.addSurface(newSurf);
}
}
}

}
catch (Exception e) // This is too general to be the only catch statement.
{
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
return null; // This should rethrow an error instead of returning null
}
return retPart;
}

零件和表面类

/** Part is a container for a 3D model consisting of a list surfaces.
*/
class Part
{
private List<Surface> surfaces = null; /**< A list of surfaces. */
public String name { get; set; } = ""; /**< The name of the object. */

/** The constructor.
*
* A name, even a blank one, must be provided. This can be changed later by direct accesss to
* the name parameter if desired.
*
*
* @Param name The name of the object.
*
* @Param surfaces A premade list of surfaces. This is usefull when copying another object.
*/
public Part(String name, List<Surface> surfaces = null)
{
this.name = name;
if (surfaces != null) this.surfaces = surfaces;
else this.surfaces = new List<Surface>();
}

/** Add a surface to the surface list.
*
* This function simply adds a surface. It does not attempt to interconnect it with other surfaces
* that already exist. Any movement of points to make room for the new face must be done on the user's
* end.
*
* @Param surface The face to add to the part.
*/
public void addSurface(Surface surface)
{
surfaces.Add(surface);
}

/** Get the surface at a specific index.
*
* Retrieve a surface at a given index. There is no structure as to the indexing of the faces.
* In order to find a specific surface, the user must iterate through the entire list of surfaces
* using whatever algorithm is desireable.
*
* @Param index The index of the surface to return
*
* @Return The surface object found at index.
*/
public Surface getSurface(int index)
{
return surfaces[index];
}

/** Get the number of surfaces in the part.
*
* @Return The number of surfaces contained within the part.
*/
public int size()
{
return surfaces.Count;
}

/** Removes a surface at a specific index.
*
* Find a surface at the specified index and remove it from list.
*
* @Param index The index of the surface to remove.
*/
public void removeSurface(int index)
{
surfaces.Remove(surfaces[index]);
}

/** Removes the specified surface.
*
* Removes the given surface specified by the user.
*
* @Param surface The surface to remove.
*/
public void removeSurface(Surface surface)
{
surfaces.Remove(surface);
}

/** Recalculates the normal vectors for each face.
*
* @Param outward If true all the normal vectors will face out from the mesh. If false, then the opposite.
*/
public void recalculateNormals(bool outward = true)
{
foreach(Surface surf in surfaces)
{
// Extract vertices
Vector3d p1 = surf.vertices[0]; // Vertex 1
Vector3d p2 = surf.vertices[1]; // Vertex 2
Vector3d p3 = surf.vertices[2]; // Vertex 3

// Create edge vectors
Vector3d l21 = new Vector3d(); // Line from Vertex 2 to Vertex 1
Vector3d l23 = new Vector3d(); // Line from Vertex 2 to Vertex 3

l21.X = p1.X - p2.X;
l21.Y = p1.Y - p2.Y;
l21.Z = p1.Z - p2.Z;
l23.X = p3.X - p2.X;
l23.Y = p3.Y - p2.Y;
l23.Z = p3.Z - p2.Z;

// Find the normal using the cross-product
Vector3d norm = new Vector3d();
norm.X = l21.Y * l23.Z - l23.Y * l21.Z;
norm.Y = l21.Z * l23.X - l23.Z * l21.X;
norm.Z = l21.X * l23.Y - l23.X * l21.Y;

norm.Normalize(); // Ensure that the vector is unit length

// Make sure the normal faces outwards
Vector3d surfVec = surf.surfaceVector();

if(surfVec.X * norm.X + surfVec.Y * norm.Y + surfVec.Z * norm.Z >= 0)
{
//Console.WriteLine("New Normal is facing outside of the mesh.");
}
else
{
//Console.Write("New Normal is facing inside of the mesh. Fixing...");
norm *= -1.0; // Flip the normal
//Console.WriteLine(" Done.");
}

// DEBUG
//Console.WriteLine(String.Format("Old Normal <{0},{1},{2}>, New Normal <{3},{4},{5}>", surf.normal.X, surf.normal.Y, surf.normal.Z, norm.X, norm.Y, norm.Z));

surf.normal = norm;
}
}
}

/** Surface is an object that contains a surface normal and a list of points making up that surface.
*/
class Surface
{
// Surface Properties, Getters, and Setters
public Vector3d normal { get; set; } = new Vector3d(); /**< The normal vector. */
public List<Vector3d> vertices { get; set; } = new List<Vector3d>(); /**< The perimeter verticies. */

// Constructors
/** Default constructor
*/
public Surface()
{
init(new Vector3d(), new List<Vector3d>());
}

/** Constructor with assignable data fields
*/
public Surface(Vector3d normal, List<Vector3d> vertices)
{
init(normal, vertices);
}

/** Returns a vector from the origin of the object to the center of the face.
*/
public Vector3d surfaceVector()
{
// Create a new vector and make sure it's components are 0
Vector3d surfVector = new Vector3d();
surfVector.X = 0;
surfVector.Y = 0;
surfVector.Z = 0;

// Sum the components of each vertext that makes up the surface
foreach (Vector3d vec in vertices)
{
surfVector += vec;
}

// Divide each component by the number of vertices
surfVector *= (1.0 / vertices.Count);

return surfVector;
}

/** The constructor helper function
*
* This function assigns the proper data to the correct members.
*
* @Param _normal The normal vector.
*
* @Param _vertices A list of vertices.
*/
private void init(Vector3d _normal, List<Vector3d> _vertices)
{
normal = _normal;
vertices = _vertices;
}
}

我不确定下一步该去哪里。这看起来有点像 off by 1 error 或者我在某处跳过数据。虽然,除了中间面的顶点,网格看起来很干净。任何想法表示赞赏。提前致谢。

最佳答案

事实证明这是一种错误。在导入二进制文件中:

if (i == 1) //<- WRONG!!!
{
// This is a normal
Vector3d norm = new Vector3d();
norm.X = xCompReal;// * scaleFactor;
norm.Y = yCompReal;// * scaleFactor;
norm.Z = zCompReal;// * scaleFactor;
//if(Math.Abs(Math.Pow(norm.X,2) + Math.Pow(norm.X, 2) + Math.Pow(norm.X, 2) - 1) > .001)
//{
// Console.WriteLine("ERROR: Improper file read. Surface normal is not a unit vector.");
//}
newSurf.normal = norm;
}

应该是

if (i == 0) //<- RIGHT!!!
{
// This is a normal
Vector3d norm = new Vector3d();
norm.X = xCompReal;// * scaleFactor;
norm.Y = yCompReal;// * scaleFactor;
norm.Z = zCompReal;// * scaleFactor;
//if(Math.Abs(Math.Pow(norm.X,2) + Math.Pow(norm.X, 2) + Math.Pow(norm.X, 2) - 1) > .001)
//{
// Console.WriteLine("ERROR: Improper file read. Surface normal is not a unit vector.");
//}
newSurf.normal = norm;
}

我只是交换法线和第一个顶点的值。

关于C# 如何解析 STL 文件。当前函数没有正确地将顶点链接到面中。算法错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68568214/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com