读写三维数据.stl文件

这篇博客详细介绍了STL文件的ASCII和二进制格式,包括它们的结构和存储方式。提供了一个C++函数用于判断文件格式,并分别给出了读取两种格式STL文件的实现。同时,还展示了如何使用C++创建CSTL类来设置和保存STL信息,包括ASCII和二进制格式的文件保存方法。
摘要由CSDN通过智能技术生成

STL文件格式有两种:ASCII字符格式,及二进制格式。

ASCII字符格式的格式如下:

solid name         // 文件名是可选的字符串
facet normal ni nj nk
outer loop
vertex v1x v1y v1z
vertex v2x v2y v2z
vertex v3x v3y v3z
endloop
endfacet
endsolid name      //结束行标志

二进制格式数据:

因为字符格式的STL文件比较大,占空间,因为有了二进制格式,且二进制的存储格式与ASCII的格式不同。二进制有80个字节作为文件头,一般都忽略掉,但开头不能使solid,不然就不能与ASCII格式相区分了。 另外,接下来4个字节是存放的三角片的个数。

UINT8[80] – Header
UINT32 – Number of triangles
 
foreach triangle
REAL32[3] – Normal vector
REAL32[3] – Vertex 1
REAL32[3] – Vertex 2
REAL32[3] – Vertex 3
UINT16 – Attribute byte count
end

读取文件之前需判断文件为哪种格式:

bool isSTLBinary(const char* fileName)
{
  bool isBinary = false;//return value
  FILE *fp = fopen(fileName, "r");
  //int errorCode = fopen_s(&fp, fileName, "r");
  
  // #ifdef __unix
  // #define fopen_s(pFile,filename, "r") ((*(pFile))=fopen((fileName),  ("r")))==NULL
  // #endif
 
  if (fp)//成功打开文件
  {
      printf("%s isSTLBinary open success\n", fileName);
      //确定文件实际大小
      fseek(fp, 0, SEEK_END);//将fp移动到文件尾部
      int fileSize = ftell(fp);//返回文档首部到fp位置大小(bytes)
      int facetNum;
      //计算标准stl二进制文件的大小
      fseek(fp, 80, SEEK_SET);//跳过文档开头的注释
      size_t t = fread(&facetNum, sizeof(int), 1, fp);//读取facet的数目并保存在facetNum中
      int standardBinaryFileSize = 80 + sizeof(int) + facetNum * 50;
      //判断是否是标准stl文件
      if (fileSize == standardBinaryFileSize)
      {
          isBinary = true;
      }
      //判断是否是非标准stl文件
      unsigned char tmpbuf[128];//如果文件过短,这里会有Bug
      t = fread(tmpbuf, sizeof(tmpbuf), 1, fp);//读取128个char大小的数据
      for (unsigned int i = 0;i<sizeof(tmpbuf);i++)
      {
          //char类型取值范围是-128~127,如果是ASCII格式,应该全是char
          if (tmpbuf[i]>127)
          {
              isBinary = true;
              break;
          }
      }
      fclose(fp);
      printf("t=%d\n", (int)t);
  }
  else
  {
    printf("%s isSTLBinary open file fail\n", fileName);
  }
  return isBinary;
}

之后再根据文件格式选择读取方法。

读取二进制文件:

void openBinaryFile(std::string fileName, std::vector<float>& positions, std::vector<float>& normals)
{
    FILE *fp = fopen(fileName.c_str(), "rb");
    int numFacet;
    //int error= fopen_s(&fp,fileName, "rb");
    
    float normal[3];
    float point1[3];
    if (fp)//成功打开文件
    {
        fseek(fp, 80, SEEK_SET);//跳过开头
        size_t t = fread(&numFacet, sizeof(int), 1, fp);//读取facet的数目
        for (int facetIndex = 0; facetIndex < numFacet;++facetIndex)
        {
            unsigned short attr; //用来储存属性,实际上把这个值丢弃了
            t = fread(normal, sizeof(float), 3, fp);//读取facet的法向量
            for (int i = 0; i < 3;++i)
            {
                t = fread(point1, sizeof(float), 3, fp);//读取vertex
                positions.push_back(point1[0] / 1000);
                positions.push_back(point1[1] / 1000);
                positions.push_back(point1[2] / 1000);
                normals.push_back(point1[0] / 1000);
                normals.push_back(point1[1] / 1000);
                normals.push_back(point1[2] / 1000);
                //printf ("%f, %f %f", point1[0], point1[1], point1[2]);
            }
            t = fread(&attr, sizeof(unsigned short), 1, fp);//读取属性
        }
        printf("t=%d\n", (int)t);
    }
    else 
    {
      printf("%s open fail\n", fileName.c_str());
    }
    fclose(fp);
}

读取ASCII文件:

void openASCIIFile(std::string fileName, std::vector<float>& positions, std::vector<float>& normals)
{
  std::ifstream ifs;
  ifs.open(fileName, std::ifstream::in);
  if (ifs.is_open())
  {
      /*略去第一行*/
      std::string line;
      getline(ifs, line);
 
      /*读取facet*/
      int readCounter = 0;//记录每行读取成功的数据项
      int lineCounter = 0;
      double x,y,z = 0;
      double pointX, pointY, pointZ = 0;
      while (!ifs.eof())
      {
        /*读取facet normal ni nj nk*/
        getline(ifs, line);
        readCounter = sscanf(line.c_str(), "%*s %*s %lf %lf %lf\n", &x, &y, &z);
        if (readCounter!=3)
        {
            // we could be in the case of a multiple solid object, where after a endfaced instead of another facet we have to skip two lines:
            //     endloop
            //   endfacet
            //endsolid     <- continue on ret==0 will skip this line
            //solid ascii  <- and this one.
            //   facet normal 0.000000e+000 7.700727e-001 -6.379562e-001
            lineCounter++;
            continue;
        }//end if
 
        getline(ifs, line);//读取outer loop
/*读取三个顶点坐标vertex v1x v1y v1z*/
        for (int i = 0; i < 3;i++)
        {
            getline(ifs, line);
            readCounter=sscanf(line.c_str(),"%*s %lf %lf %lf", &pointX, &pointY, &pointZ);
            positions.push_back(pointX);
            positions.push_back(pointY);
            positions.push_back(pointZ);
            normals.push_back(pointX);
            normals.push_back(pointY);
            normals.push_back(pointZ);
        }
        getline(ifs, line);//读取“endloop”
        getline(ifs, line);//读取"endfacet"
        lineCounter += 7;
    }// end while (ifs.eof())
  }//end if (ifs.is_open())
  else
  {
      return;
  }
  ifs.close();
}

STL 类

class CSTL
{
private:
float *vx;
float *vy;
float *vz;

int *triaV1;
int *triaV2;
int *triaV3;

int m_TriaNum;
public:

CSTL();
~CSTL();
public:
bool SetStlInf(float *px,float *py,float *pz,
       int *TriaNum1,int *TriaNum2,int *TriaNum3,int TriaTotalNum);
bool SaveStlBinary(char *pathname,char * filename);
bool SaveStlASCII(char *pathname,char * filename);


};


CSTL::CSTL()
{
}

CSTL::~CSTL()
{
}

设置STL信息

bool CSTL::SetStlInf(float *px, float *py, float *pz,
      int *TriaNum1, int *TriaNum2, int *TriaNum3, int TriaTotalNum)
{
bool success = false;

vx = px;
vy = py;
vz = pz;

triaV1 = TriaNum1;
triaV2 = TriaNum2;
triaV3 = TriaNum3;

m_TriaNum = TriaTotalNum;

success = true;
return success;
}

保存STL ASCLL 格式文件

bool CSTL::SaveStlASCII(char *pathname,char * filename)
{
bool suc = true;
char *savename = new char[100];
sprintf(savename,"%s%s.ast",pathname,filename);

char *fileInf = new char[200];
sprintf(fileInf,"solid %s.ast  %s",filename,"by master");

FILE *fp = fopen(savename,"w");
fprintf(fp,"%s\n",fileInf);
delete []savename;

for(int i=0;i<m_TriaNum;i++)
{
  int id= triaV1[i];
  float v1x = vx[id];
  float v1y = vy[id];
  float v1z = vz[id];
  
  id= triaV2[i];
  float v2x = vx[id];
  float v2y = vy[id];
  float v2z = vz[id];

  id= triaV3[i];
  float v3x = vx[id];
  float v3y = vy[id];
  float v3z = vz[id];

  float nx = (v1y-v3y)*(v2z-v3z)-(v1z-v3z)*(v2y-v3y);
  float ny = (v1z-v3z)*(v2x-v3x)-(v2z-v3z)*(v1x-v3x);
  float nz = (v1x-v3x)*(v2y-v3y)-(v2x-v3x)*(v1y-v3y);

  float nxyz = sqrt(nx*nx+ny*ny+nz*nz);

  fprintf(fp,"facet normal %f %f %f\n",nx/nxyz,ny/nxyz,nz/nxyz);
  fprintf(fp,"outer loop\n");
  fprintf(fp,"vertex %f %f %f\n",v1x,v1y,v1z);
  fprintf(fp,"vertex %f %f %f\n",v2x,v2y,v2z);
  fprintf(fp,"vertex %f %f %f\n",v3x,v3y,v3z);
  fprintf(fp,"endloop\n");
  fprintf(fp,"endfacet\n");

}
sprintf(fileInf,"endsolid %s.ast  %s",filename,"by master");
fprintf(fp,"%s\n",fileInf);
fclose(fp);

delete []fileInf;

return suc;
}

保存STL 二进制格式文件

bool CSTL::SaveStlBinary(char *pathname,char * filename)
{

bool suc = true;
char *savename = new char[100];
sprintf(savename,"%s%s.stl",pathname,filename);

char *fileInf = new char[200];
sprintf(fileInf,"solid %s.stl  %s",filename,"by master");

FILE *fp = fopen(savename,"wb");
delete []savename;


float* dat = new float[12];

fwrite(fileInf,sizeof(char),80,fp);
fwrite(&m_TriaNum,sizeof(int),1,fp);

for(int i=0;i<m_TriaNum;i++)
{
  int id= triaV1[i];
  float v1x = vx[id];
  float v1y = vy[id];
  float v1z = vz[id];
  
  id= triaV2[i];
  float v2x = vx[id];
  float v2y = vy[id];
  float v2z = vz[id];

  id= triaV3[i];
  float v3x = vx[id];
  float v3y = vy[id];
  float v3z = vz[id];

  float nx = (v1y-v3y)*(v2z-v3z)-(v1z-v3z)*(v2y-v3y);
  float ny = (v1z-v3z)*(v2x-v3x)-(v2z-v3z)*(v1x-v3x);
  float nz = (v1x-v3x)*(v2y-v3y)-(v2x-v3x)*(v1y-v3y);

  float nxyz = sqrt(nx*nx+ny*ny+nz*nz);
  
  dat[0] = nx/nxyz;
  dat[1] = ny/nxyz;
  dat[2] = nz/nxyz;

  dat[3] = v1x;
  dat[4] = v1y;
  dat[5] = v1z;

  dat[6] = v2x;
  dat[7] = v2y;
  dat[8] = v2z;

  dat[9] = v3x;
  dat[10] = v3y;
  dat[11] = v3z;

  fwrite(dat,sizeof(float),12,fp);
  fwrite("wl",sizeof(char),2,fp);

}

fclose(fp);

delete []dat;
delete []fileInf;

return suc;

}

https://blog.csdn.net/lo_ve18/article/details/120566054?

https://blog.csdn.net/junpengxue/article/details/9951057?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值