如何读取STL文件?

STL文件有两种文件格式,分别是二进制的stl和Ascii格式的stl。下面将针对这文件的两个格式,用C语言分别写出一段简单的示例代码。

一、读取二进制的STL文件

(1)二进制stl文件的结构

文件起始的80个字节是文件头,用于存贮文件名;
 紧接着用 4 个字节的整数来描述模型的三角面片个数, 后面逐个给出每个三角面片的几何信息。每个三角面片占用固定的50个字节,依次是: 
3个4字节浮点数(角面片的法矢量) 
3个4字节浮点数(1个顶点的坐标)
 3个4字节浮点数(2个顶点的坐标) 
3个4字节浮点数(3个顶点的坐标)
三角面片的最后2个字节用来描述三角面片的属性信息,一般只保留位置,具体数据不用管。 
一个完整二进制STL文件的大小为三角形面片数乘以 50再加上84个字节。
(2)C语言读取代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

//文件头,共84字节
struct Head
{
	char partName[80];//零件名称
	int  faceNum;//面的数目
};

//点,三个float类型的,大小为12字节
struct Point
{
	float x;
	float y;
	float z;
};

//法线
struct Normal
{
	float i;
	float j;
	float k;
};

//三角面,由一个法线,三个点,和一个两字节的保留项,一共50字节
struct Face
{
	Normal normal;
	Point  p1;
	Point  p2;
	Point  p3;
	char  info[2];//保留数据,可以不用管
};

int main()
{
	Head head;
	FILE *fp;
	char fileName[128];

	printf("请输入文件名:\n");
	gets_s(fileName);//控制台里输入文件名称
	fp = fopen(fileName,"rb");

	if (fp != NULL)
	{
		fread(head.partName, 80, 1, fp);//获取部件名
		fread(&head.faceNum, 4, 1, fp);//获取三角面片的数目
	}

	Face *faces = (Face*)malloc(head.faceNum * sizeof(Face));//根据三角面片的数目,创建一个Face类型的数组

	//循环读取三角面片数据
	for (int i = 0;i < head.faceNum;i++)
	{
		fread(&faces[i].normal, 12, 1, fp);//读取法线数据
		fread(&faces[i].p1, 12, 1, fp);//读取顶点1的数据
		fread(&faces[i].p2, 12, 1, fp);//读取顶点2的数据
		fread(&faces[i].p3, 12, 1, fp);//读取顶点3的数据
		fread(&faces[i].info,2, 1, fp);//读取保留项数据,这一项一般没什么用,这里选择读取是为了移动文件指针
	}

	fclose(fp);

	printf("STL文件解析如下:\n");
	printf("三角形数目:");
	printf("%d\n", head.faceNum);
	printf("各面的顶点和法线数据如下:\n");

	//在控制台上输出STL文件的数据
	for (int i = 0;i < head.faceNum;i++)
	{
		printf("面%d\n", i + 1);
		printf("法线:\n");
		printf("%f  %f  %f\n", faces[i].normal.i, faces[i].normal.j, faces[i].normal.k);
		printf("顶点数据:\n");
		printf("%f  %f  %f\n", faces[i].p1.x, faces[i].p1.y, faces[i].p1.z);
		printf("%f  %f  %f\n", faces[i].p2.x, faces[i].p2.y, faces[i].p2.z);
		printf("%f  %f  %f\n", faces[i].p2.x, faces[i].p2.y, faces[i].p2.z);
	}

	return 0;
}

(3)程序运行效果

二、读取ASCII格式的stl文件
(1)ASCII格式的stl文件结构
ASCII码格式的STL文件逐行给出三角面片的几何信息,每一行以1个或2个关键字开头。 在STL文件中的三角面片的信息单元 facet 是一个带矢量方向的三角面片, STL三维模型就是由一系列这样的三角面片构成。 整个STL文件的首行给出了文件路径及文件名。 在一个 STL文件中,每一个facet由7 行数据组成, facet normal 是 三角面片指向实体外部的法矢量坐标, outer loop 说明随后的3行数据分别是三角面片的3个顶点坐标,3顶点沿指向实体外部的法矢量方向逆时针排列。ASCII格式的 STL 文件结构如下:
solid filename//第一行,"solid"标识+上文件名,文件名可以是任意字符
facetnormal i j k//第二行,“facetnormal”标识+法线数据i,j,k
outorloop//第三行,“outorloop”标识
vertex x y z//第四行,“vertex”标识+第一个顶点数据x,y,z
vertex x y z//第五行,“vertex”标识+第二个顶点数据
vertex x y z//第六行,"vertex"标识+第三个顶点数据
endloop//第七行,“endloop”标识
endfacet//第八行,"endfacet"标识
、、、、//其他三角面
endsolid filename//文件末尾,“endsolid”标识+文件名
下图是一个实例,保存的是一个四面体的数据:


(2)C语言读取代码
#include<stdio.h>
#include<stdlib.h>

char partName[80];//文件头,

struct Normal
{
	float i;
	float j;
	float k;
};

struct Vertrex
{
	float x;
	float y;
	float z;
};

struct Face
{
	Normal normal;
	Vertrex vertex1;
	Vertrex vertex2;
	Vertrex vertex3;
};

//接收三角片面的链表结点
struct Faces
{
	Face data;
	Faces *pointer;
};

int main()
{
	FILE *fp;
	char fileName[64];
	Faces *faces = (Faces*)malloc(sizeof(Faces));//链表的表头
	Faces *p = faces;
	Faces *q;
	printf("请输入文件名:\n");
	gets_s(fileName);

	fp = fopen(fileName, "r");

	if (fp)
	{
		char str[24];
		fscanf(fp, "%s", str);//该代码的作用仅是移动文件指针,使指针跳过标识符“solid”,将文件指针移到文件名称的位置
		fscanf(fp, "%s", partName);//读入文件名称

		//循环读取三角片面的法线和顶点数据
		while (!feof(fp))
		{
			p->pointer= (Faces*)malloc(sizeof(Faces));//创建新结点,用于接收数据
			q = p;
			p = p->pointer;
			fscanf(fp, "%s", str);//使文件指针跳过标识符“facetnormal”,指到法线数据部分
			fscanf(fp, "%f%f%f", &p->data.normal.i, &p->data.normal.j, &p->data.normal.k);//读取法线数据
			fscanf(fp, "%s", str);//使文件指针跳过标识符“facetloop”
			fscanf(fp, "%s", str);//使文件指针跳过标识符“vertex”,指到顶点1的数据部分
			fscanf(fp, "%f%f%f", &p->data.vertex1.x, &p->data.vertex1.y, &p->data.vertex1.z);
			fscanf(fp, "%s", str);//使文件指针跳过标识符“vertex”,指到顶点2的数据部分
			fscanf(fp, "%f%f%f", &p->data.vertex2.x, &p->data.vertex2.y, &p->data.vertex2.z);
			fscanf(fp, "%s", str);//使文件指针跳过标识符“vertex”,指到顶点3的数据部分
			fscanf(fp, "%f%f%f", &p->data.vertex3.x, &p->data.vertex3.y, &p->data.vertex3.z);
			fscanf(fp, "%s", str);//使文件指针跳过标识符“endloop”
			fscanf(fp, "%s", str);//使文件指针跳过标识符“endfacet“
		}
		free(q->pointer);//由于文件末尾有一行字符”endsolid .....“,造成多创建了一个结点,该结点的数据是无用的,因此要释放
		q->pointer = NULL;//最后一个结点的数据释放完之后,将指向最后结点的指针置为NULL,方便遍历链表

	}
	else
	{
		printf("打开文件失败!");
	}

	p = faces->pointer;//获取链表第二个结点的指针
	while (p!=NULL)
	{
		//循环输出链表
		printf("法线:\n");
		printf("%f  %f  %f\n", p->data.normal.i, p->data.normal.j, p->data.normal.k);
		printf("顶点:\n");
		printf("%f  %f  %f\n", p->data.vertex1.x, p->data.vertex1.y, p->data.vertex1.z);
		printf("%f  %f  %f\n", p->data.vertex2.x, p->data.vertex2.y, p->data.vertex2.z);
		printf("%f  %f  %f\n", p->data.vertex3.x, p->data.vertex3.y, p->data.vertex3.z);
		p = p->pointer;
	}

	return 0;
}

(3)程序运行效果

  • 18
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shifenglv

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值