- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在开发一个需要在 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/
我在 Web 应用程序中尝试了一些字节码操作,到目前为止,效果很好。现在我需要在代码中的某些特定位置注入(inject)一些字节代码。我用 switch 语句和 method.inserAt(); 尝
我正在尝试对数组中的对象值求和,然后计算每个值相对于总数的百分比。 这里是数组: [ { "ratiototale": [ [ { "0":
我在接口(interface)中有以下方法.. Task> SearchAsync(TU searchOptions); 效果很好。 现在我正在尝试进行单元测试以测试何时出现问题 - 代码会抛出异常。
我似乎无法让 reveal 开始工作,我已经严格遵守所有内容,添加 modernizr,添加插件,添加 css(reveal 显然包含在 foundation 中),在开始时初始化 foundatio
我正在尝试移植 raspberrypi's userspace 的位从 C 到 golang 的代码,我遇到了一个涉及 ioctl() 的程序。 我在执行 C 代码时遇到了问题 #define MAJ
我一直在尝试用纯 Java 制作一个简单的游戏,但在绘图时遇到了问题。我正在尝试保持相对较高的帧速率,但是 JFrame.repaint() 不能被“强制”并且只是在下一个可用机会重绘帧的请求这一事实
给定一个字符串 "2*(i+j) = 20 我想返回 ((lambda x: x >= 20), ['i']) 我可以然后直接提供给 constraint。 最佳答案 您正在寻找 eval 的替代方法
我对改进我的 javascript 代码以使其成为正确的 OOP 很感兴趣....目前我倾向于做这样的事情: jQuery(document).ready(function () { Page
我有一个调用两个动画 Action 的事件监听器。不幸的是,它们的开始有少量错开(例如,函数中的第一个首先开始)。 有谁知道正确同步它们的方法吗? 这是我的代码: $("#nav ul li a").
我只需要检查目录是否存在!但是,如果目录是“E:\Test”,其中 E: 是 CD/DVD 驱动器,并且上面没有插入磁盘,我会看到以下 Delphi 和 Windows 问题。 第一种方法: func
同样的问题: https://stackoverflow.com/questions/11294207/exchange-web-services-argumentexception-using-my
如果您跳转到 this question 中的第一个答案你会看到他使用 Employee.prototype = new Person(); 将 Person.prototype 继承到 Employ
我需要知道如何正确地遍历元素的 ArrayList 并计算元素在列表中出现的次数,而无需事先知道该元素。我尝试了几种方法并且有一种目前有效,但我觉得它很丑陋且不合适。 为了更深入地解释,我有一个 ja
我有一个用 Python 编写的(非常基本但工作完美的)AWS lambda 函数,但是它具有嵌入式凭证以连接到:1)外部网络服务2) DynamoDB 表。 该函数的作用相当基本:它针对服务发布登录
我很好奇 Tornado 推荐的查询 Redis(或任何数据库)的方法是什么。 我见过一些像 https://gist.github.com/357306 这样的例子但他们似乎都在使用对 redis
这更像是一个“我做得对吗”的问题。 快速背景故事:我已经构建了一个 gradle 插件(在一个独立的 gradle/groovy 项目中)。我在另一个 java 项目中使用它。客户项目通过以下方式引用
在我的代码中,我有: function handleMessage() { const twilio = require('twilio')(process.env.TWILIO_ACCOUNT_
我正在努力在 Linux 中刷新嵌入式设备的先前 ROM 转储。我以前的转储包含 oob 数据。我是用nandwrite -n -N -o/dev/mtd0 backup.bin写的,然后再做一次RO
我正在尝试使用 go 为 react-router 提供服务,我已经做到了,但我遇到了麻烦,我认为我做的方式不正确,或者不完整。我正在使用 Mux。我遇到的麻烦是当我按下 时在我的应用程序中,它正确
我正在尝试 promise JSON.parse 方法,但不幸的是没有任何运气。这是我的尝试: Promise.promisify(JSON.parse, JSON)(data).then((resu
我是一名优秀的程序员,十分优秀!