将外部支持者生成为用于 3D 打印的网格 [英] Generating outside supporters into mesh for 3D printing

查看:24
本文介绍了将外部支持者生成为用于 3D 打印的网格的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

序言

这是我试图重新询问封闭的

对于粗糙,我们需要添加尽可能少的材料,并且仍然足够坚固以将我们的网格固定到位而不会弯曲.最重要的是,我们需要削弱网格附近的支撑,以便在打印后轻松折断.

不要忘记形状和位置取决于许多因素,例如使用的材料和技术、热流.

问题:

为了将这个庞大的主题缩小为可回答的问题,让我们只关注这个问题:

如何合并 3D 三角网格(边界表示,如

此形状将从某个基准平面 (Z=0) 开始,然后向上移动直到碰到网格.然而,为了使这项工作起作用,支撑必须在网格和自身之间有一个小的间隙,我们将在那里添加我们弱化的关节结构,并在后面添加网格.

  • 支持模式

    这里有很多选择,所以我选择了最简单的(但不是防犯规),那就是将支架放置在一个均匀的网格中,支架之间的距离保持不变grid.

    所以简单地从基础平面上的每个网格位置向上投射一条射线,并检查与网格的相交.如果找到,将支撑放置在该位置,高度在交点下方 gap.

  • 关节

    这个想法是将非常薄的支撑风扇连接成锥形连接&以小于 45 度角覆盖主支撑棱镜上方的支撑表面(因此 gap 应该足够大,以这种方式覆盖 grid 距离).

    这里的主要问题是我们必须细分要连接的三角形,以便满足 STL 网格属性.为了解决连接问题(避免 STL 的孔或破坏连接要求),我们可以使用不同的实体作为支撑,而不同的实体用于我们的网格.这也将使我们能够在不重新对它们进行三角测量的情况下触摸表面,从而使这项任务变得容易得多.

    为简单起见,我选择了四面体形状,它易于从三角形构建,并且在网格/支撑接头处也存在弱点.

  • 因此,让我们进行一些测试STL 网格并将其放置在我们的基准平面上方:

    并放置我们的主要支持:

    还有关节:

    这里是这个 STL3D.h 的 VCL/C++ 代码:

    //---------------------------------------------------------------------//--- 简单的 STL 3D 网格 ----------------------------------------------------//---------------------------------------------------------------------------#ifndef _STL3D_h#define _STL3D_h//---------------------------------------------------------------------------#ifdef ComctrlsHPPTProgressBar *progress=NULL;//加载非常大的 STL 文件的进度条#万一void _progress_init(int n);void _progress (int ix);无效_progress_done();//---------------------------------------------------------------------------class STL3D//STL 3D 网格{民众:双中心[3],大小[3],rmax;//bbox 中心,一半大小,max(size[])结构_fac{浮动 p[3][3];//三角形顶点逆时针顺序浮动 n[3];//三角形单元法线指向字属性;_fac() {}_fac(_fac& a) { *this=a;}~_fac() {}_fac* 运算符 = (const _fac *a) { *this=*a;返回这个;}//_fac* operator = (const _fac &a) { ...copy... return this;}void compute()//计算法线{浮动 a[3],b[3];vectorf_sub(a,p[1],p[0]);vectorf_sub(b,p[2],p[1]);vectorf_mul(n,a,b);vectorf_one(n,n);}double intersect_ray(double *pos,double *dir)//返回 -1 或三角形和单位光线相交的距离{双 p0[3],p1[3],p2[3];//输入三角形顶点双 e1[3],e2[3],pp[3],qq[3],rr[3];//dir 必须是单位向量!!!双 t,u,v,det,idet;//获得积分vector_ld(p0,p[0][0],p[0][1],p[0][2]);vector_ld(p1,p[1][0],p[1][1],p[1][2]);vector_ld(p2,p[2][0],p[2][1],p[2][2]);//计算射线三角形交点vector_sub(e1,p1,p0);vector_sub(e2,p2,p0);//计算平面法向量vector_mul(pp,dir,e2);det=vector_mul(e1,pp);//射线平行于平面如果 (fabs(det)<1e-8) 返回 -1.0;idet=1.0/det;vector_sub(rr,pos,p0);u=vector_mul(rr,pp)*idet;如果 ((u<0.0)||(u>1.0)) 返回 -1.0;vector_mul(qq,rr,e1);v=vector_mul(dir,qq)*idet;如果 ((v<0.0)||(u+v>1.0)) 返回 -1.0;//距离t=vector_mul(e2,qq)*idet;如果 (t<0.0) t=-1.0;返回 t;}};列表<_fac>事实;//面孔STL3D() { 重置();}STL3D(STL3D& a) { *this=a;}~STL3D() {}STL3D* 运算符 = (const STL3D *a) { *this=*a;返回这个;}//STL3D* operator = (const STL3D &a) { ...copy... return this;}void reset(){ fac.num=0;计算();}//清除 STL无效绘制();//渲染 STL 网格(OpenGL)void draw_normals(浮动大小);//渲染 STL 法线 (OpenGL)无效计算();//计算bbox无效计算法线();//从点重新计算法线无效支持(reper &obj);//计算 obj 放置在基准平面 z=0 上方的支撑无效负载(AnsiString名称);无效保存(AnsiString名称);};//---------------------------------------------------------------------------void STL3D::draw(){_fac * f;int i,j;字节 r,g,b;glBegin(GL_TRIANGLES);for (f=fac.dat,i=0;in);如果 (f-> attr<32768){r= f-> attr &31;r<<=3;g=(f->attr>>5)&31;g<<=3;b=(f->attr>>10)&31;b<<=3;glColor3ub(r,g,b);}对于 (j=0;j<3;j++) glVertex3fv(f->p[j]);}glEnd();}//---------------------------------------------------------------------------void STL3D::draw_normals(浮动大小){_fac * f;国际我;浮动 a[3],b[3];glBegin(GL_LINES);for (f=fac.dat,i=0;ip[0],f->p[1]);vectorf_add(a,a,f->p[2]);vectorf_mul(a,a,1.0/3.0);vectorf_mul(b,f->n,size);glVertex3fv(a);vectorf_add(b,b,a);glVertex3fv(b);}glEnd();}//---------------------------------------------------------------------------void STL3D::compute(){_fac * f;输入 i,j,k;双 p0[3],p1[3];vector_ld(center,0.0,0.0,0.0);vector_ld(size,0.0,0.0,0.0);rmax=0.0;如果(fac.num==0)返回;//框对于 (k=0;k<3;k++) p0[k]=fac.dat[0].p[0][k];对于 (k=0;k<3;k++) p1[k]=fac.dat[0].p[0][k];for (f=fac.dat,i=0;if->p[j][k]) p0[k]=f->p[j][k];如果(p1[k]<f->p[j][k])p1[k]=f->p[j][k];}矢量添加(中心,p0,p1);vector_mul(中心,中心,0.5);vector_sub(大小,p1,p0);vector_mul(大小,大小,0.5);rmax=大小[0];如果 (rmaxattr==0) f->attr=32768;}//---------------------------------------------------------------------------void STL3D::compute_normals(){_fac * f;国际我;for (f=fac.dat,i=0;icompute();}//---------------------------------------------------------------------------void STL3D::supports(reper &obj){_fac *f,ff;输入 i,j,k;双 p[3],dp[3],x0,y0,h0,x1,y1,x2,y2,h1,t;//首先是一些配置值const WORD attr0=31<<10;//support attr 应该不同于jointconst WORD attr1=31<<5;//关节属性应该不同于网格,支持const double grid0=8.0;//支架之间的距离const double grid1=2.0;//关节之间的距离const double gap=grid0/tan(45.0*deg);//主支撑与网格之间的距离(关节尺寸)const double ha=1.0;//主要支撑边尺寸//不要弄乱这些const double hx= ha*cos(60.0*deg);//x 中主支撑的一半大小const double hy=0.5*ha*sin(60.0*deg);//y 中主支撑的一半大小const double grid2=0.4*hy;//关节基点之间的距离const double ga=2.0*grid2*grid1/grid0;//主要支撑边尺寸const double gx=hx*grid2/grid0;//x 中关节支撑的一半大小const double gy=hy*grid2/grid0;//y 中关节支撑的一半大小//如果不使用矩阵,则不需要应用放置 obj(可能会失去一些准确性)for (f=fac.dat,i=0;ip[j][k];//浮动->双obj.l2g(p,p);对于(k=0;k<3;k++)f->p[j][k]=p[k];//double->float}对于(k=0;k<3;k++)p[k]=f->n[k];//浮动->双obj.l2g_dir(p,p);对于(k=0;k<3;k++)f->n[k]=p[k];//double->float} 计算();//创建支撑对于 (x0=center[0]-size[0]+(0.5*grid0);x0<=center[0]+size[0]-(0.5*grid0);x0+=grid0)对于 (y0=center[1]-size[1]+(0.5*grid0);y0<=center[1]+size[1]-(0.5*grid0);y0+=grid0){//在 Z+ 方向投射光线 x0,y0,0 以检查网格交点以计算支撑高度 h0h0=中心[2]+大小[2]+1e6;vector_ld(p,x0,y0,0.0);vector_ld(dp,0.0,0.0,+1.0);for (f=fac.dat,i=0;iintersect_ray(p,dp);如果 ((t>=0.0)&&(t<h0)) h0=t;}如果 (h0>center[2]+size[2]+1e5) 继续;//跳过不相交的光线h0-=间隙;如果 (h0<0.0) h0=0.0;//主支撑棱镜ff.attr=attr0;//边ff.attr=attr0;vectorf_ld(ff.p[0],x0-hx,y0-hy,0.0);vectorf_ld(ff.p[1],x0+hx,y0-hy,0.0);vectorf_ld(ff.p[2],x0-hx,y0-hy,h0);ff.compute();fac.add(ff);vectorf_ld(ff.p[0],x0+hx,y0-hy,0.0);vectorf_ld(ff.p[1],x0+hx,y0-hy,h0);vectorf_ld(ff.p[2],x0-hx,y0-hy,h0);ff.compute();fac.add(ff);vectorf_ld(ff.p[0],x0-hx,y0-hy, h0);vectorf_ld(ff.p[1],x0,y0+hy,0.0);vectorf_ld(ff.p[2],x0-hx,y0-hy,0.0);ff.compute();fac.add(ff);vectorf_ld(ff.p[0],x0-hx,y0-hy, h0);vectorf_ld(ff.p[1],x0,y0+hy,h0);vectorf_ld(ff.p[2],x0,y0+hy,0.0);ff.compute();fac.add(ff);vectorf_ld(ff.p[0],x0,y0+hy,h0);vectorf_ld(ff.p[1],x0+hx,y0-hy,0.0);vectorf_ld(ff.p[2],x0,y0+hy,0.0);ff.compute();fac.add(ff);vectorf_ld(ff.p[0],x0,y0+hy,h0);vectorf_ld(ff.p[1],x0+hx,y0-hy,h0);vectorf_ld(ff.p[2],x0+hx,y0-hy,0.0);ff.compute();fac.add(ff);//基本三角形vectorf_ld(ff.p[0],x0,y0+hy,0.0);vectorf_ld(ff.p[1],x0+hx,y0-hy,0.0);vectorf_ld(ff.p[2],x0-hx,y0-hy,0.0);ff.compute();fac.add(ff);vectorf_ld(ff.p[0],x0-hx,y0-hy, h0);vectorf_ld(ff.p[1],x0+hx,y0-hy,h0);vectorf_ld(ff.p[2],x0,y0+hy,h0);ff.compute();fac.add(ff);//关节对于 (x1=x0-(0.5*grid0),x2=x0-(0.5*grid2);x1<=x0+(0.5*grid0);x1+=grid1,x2+=ga)对于 (y1=y0-(0.5*grid0),y2=y0-(1.9*grid2);y1<=y0+(0.5*grid0);y1+=grid1,y2+=ga){//在 Z+ 方向投射光线 x1,y1,0 以检查网格交点以计算关节高度 h1h1=h0+间隙+1e6;vector_ld(p,x1,y1,0.0);vector_ld(dp,0.0,0.0,+1.0);for (f=fac.dat,i=0;iintersect_ray(p,dp);如果 ((t>=0.0)&&(t<h1)) h1=t;}如果 (h1>h0+gap+1e5) 继续;//跳过不相交的光线//四面体关节ff.attr=attr1;//基础三角形vectorf_ld(ff.p[0],x2 ,y2+gy,h0);vectorf_ld(ff.p[1],x2+gx,y2-gy,h0);vectorf_ld(ff.p[2],x2-gx,y2-gy,h0);ff.compute();fac.add(ff);//边vectorf_ld(ff.p[0],x2+gx,y2-gy,h0);vectorf_ld(ff.p[1],x2,y2+gy,h0);vectorf_ld(ff.p[2],x1,y1,h1);ff.compute();fac.add(ff);vectorf_ld(ff.p[0],x2 ,y2+gy,h0);vectorf_ld(ff.p[1],x2-gx,y2-gy,h0);vectorf_ld(ff.p[2],x1,y1,h1);ff.compute();fac.add(ff);vectorf_ld(ff.p[0],x2+gx,y2+gy,h0);vectorf_ld(ff.p[1],x2-gx,y2-gy,h0);vectorf_ld(ff.p[2],x1,y1,h1);ff.compute();fac.add(ff);}}//如果不使用矩阵,则不需要反向放置 obj(可能会失去一些准确性)for (f=fac.dat,i=0;ip[j][k];//浮动->双obj.g2l(p,p);对于(k=0;k<3;k++)f->p[j][k]=p[k];//double->float}对于(k=0;k<3;k++)p[k]=f->n[k];//浮动->双obj.g2l_dir(p,p);对于(k=0;k<3;k++)f->n[k]=p[k];//double->float} 计算();}//---------------------------------------------------------------------------void STL3D::load(AnsiString 名称){int adr,siz,hnd;字节 *dat;AnsiString lin,s;int i,j,l,n;_fac f;重启();f.属性=0;尺寸=0;hnd=FileOpen(name,fmOpenRead);如果 (hnd<0) 返回;siz=FileSeek(hnd,0,2);FileSeek(hnd,0,0);数据=新字节[大小];if (dat==NULL) { FileClose(hnd);返回;}FileRead(hnd,dat,siz);文件关闭(hnd);广告=0;s=txt_load_str(dat,siz,adr,true);//ASCII如果(s==固体"){_progress_init(siz);int progress_cnt=0;对于 (adr=0;adr=128) { progress_cnt=0;_progress(adr);}lin=txt_load_lin(dat,siz,adr,true);for (i=1,l=lin.Length();i<=l;){s=str_load_str(lin,i,true);if (s=="solid") { name=str_load_str(lin,i,true);休息;}if (s=="endsolid") 中断;如果(s==方面"){j=0;s=str_load_str(lin,i,true);f.n[0]=str2num(str_load_str(lin,i,true));f.n[1]=str2num(str_load_str(lin,i,true));f.n[2]=str2num(str_load_str(lin,i,true));}如果(s==顶点")如果(j<3){f.p[j][0]=str2num(str_load_str(lin,i,true));f.p[j][1]=str2num(str_load_str(lin,i,true));f.p[j][2]=str2num(str_load_str(lin,i,true));j++;if (j==3) fac.add(f);}休息;}}}//二进制别的{广告=80;n=((DWORD*)(dat+adr))[0];adr+=4;fac.allocate(n);fac.num=0;_progress_init(n);int progress_cnt=0;对于 (i=0;isiz) 中断;//错误progress_cnt++;if (progress_cnt>=128) { progress_cnt=0;_progress(i);}f.n[0]=((float*)(dat+adr))[0];adr+=4;f.n[1]=((float*)(dat+adr))[0];adr+=4;f.n[2]=((float*)(dat+adr))[0];adr+=4;对于 (j=0;j<3;j++){f.p[j][0]=((float*)(dat+adr))[0];adr+=4;f.p[j][1]=((float*)(dat+adr))[0];adr+=4;f.p[j][2]=((float*)(dat+adr))[0];adr+=4;}f.attr=((WORD*)(dat+adr))[0];adr+=2;//属性fac.add(f);}}_progress_done();删除[]数据;计算();}//---------------------------------------------------------------------------void STL3D::save(AnsiString 名称){//去做}//---------------------------------------------------------------------------void _progress_init(int n){#ifdef ComctrlsHPP如果(进度==NULL)返回;进度->位置=0;进度->最大= n;进度->可见=真;#万一}//---------------------------------------------------------------------------void _progress (int ix){#ifdef ComctrlsHPP如果(进度==NULL)返回;进度->位置= ix;进度->更新();#万一}//---------------------------------------------------------------------------无效_progress_done(){#ifdef ComctrlsHPP如果(进度==NULL)返回;进度->可见=假;#万一}//---------------------------------------------------------------------------#万一//---------------------------------------------------------------------------

    用法很简单:

    #include "STL3D.h"//STL网格(这是重要的东西)STL3D网格;//点云和四面体网格mesh.load("space_invader_magnet.stl");网格.支持(对象);//obj 是持有 4x4 统一放置矩阵的对象,如果您已经放置了 STL 而不是不需要它

    我使用了很多来自我的 OpenGL 引擎的东西,比如动态 List<> 模板:


    Listxxx; 等同于 double xxx[];
    xxx.add(5);5 添加到列表末尾
    xxx[7] 访问数组元素(安全)
    xxx.dat[7] 访问数组元素(不安全但快速的直接访问)
    xxx.num 是数组实际使用的大小
    xxx.reset() 清空数组并设置xxx.num=0
    xxx.allocate(100)100 个项目预分配空间

    或向量和矩阵数学(vectorf_float*vector_double 一起使用),这不是太重要了.如果您需要数学,请参阅:

    • 在设计支撑时要记住,您应该满足打印过程的正确缠绕规则 (CCW) 和法线方向 (out) ...

      Prologue

      This is my attempt to re-ask the closed Generating supporters for 3D printing as it is interesting question but lacking important details ... This is intended as Q&A and currently I am working on the code for the answer but feel free to answer (I accept the best answer).

      Problem description

      OK here some basic info about the problem:

      As this is a huge problem I will focus on the generic mesh/support-pattern merging geometry problem.

      In a nutshell If we want to print any mesh we can do it only if it is connected to the starting plane up to angle of ~45 degrees (+/- for different printing technologies). So if we got parts that are not connected to this plane we need to create a bridge that will hold/connect it to it. Something like this (image taken from the page linked above):

      Of coarse we need to add as small amount of material possible and still has it strong enough to hold our mesh in place without bending. On top of all this we need to weaken the support near the mesh so it can be break off after printing easily.

      Do not forget that shape and placement is dependent on many things like material and technology used, heat flow.

      Question:

      To narrow this huge topic to answerable question let us focus solely on this problem:

      How to merge 3D triangulated mesh (boundary representation like STL) with predefined support pattern (like 3 side prism) connecting it from defined plane perpendicularly ?

      Using simple C++.

      解决方案

      OK lets start with the absolute basics.

      1. support shape

        You can use any shape to meet the specifics of used printing technology. The easiest to generate within STL is 3 side prism like shape which contains 2 triangular bases (top and bottom) and 3 sides all of which have 2 triangles. So 8 triangles total.

        This shape will start at some base plane (Z=0) and will go up until it hits the mesh. However to make this work the support must have a small gap between mesh and itself where we will add our weakened joint structure with mesh latter on.

      2. support pattern

        there are a lot of options here so I chose the simplest (not foul proof however) and that is to place the supports in a uniform grid with constant distance grid between the supports.

        so simply cast a ray from each grid position on the base plane in up direction and check for intersection with mesh. If found place the support at that position with height just gap below the intersection point.

      3. Joints

        The idea is to join fan of very thin supports in cone like shape connecting & covering the supported surface above main support prism with less than 45 deg angle (so the gap should be big enough to cover grid distance in such manner).

        The main problem here is that we must subdivide the triangles we are connecting to so we meet the STL mesh properties. To solve the connection problem (avoid holes or breaking connection requirements of the STL) we can use different solid for supports and different for our mesh. That will also allow us to touch surfaces without re-triangulating them making this a lot easier task.

        For simplicity I chose tetrahedron shape which is simple to construct from triangles and also present weakness at the mesh/support joint.

      So let us take some test STL mesh and place it above our base plane:

      and place our main supports:

      and also the joints:

      Here VCL/C++ code for this STL3D.h:

      //---------------------------------------------------------------------------
      //--- simple STL 3D mesh ----------------------------------------------------
      //---------------------------------------------------------------------------
      #ifndef _STL3D_h
      #define _STL3D_h
      //---------------------------------------------------------------------------
      #ifdef ComctrlsHPP
      TProgressBar *progress=NULL;        // loading progress bar for realy big STL files
      #endif
      void _progress_init(int n);
      void _progress     (int ix);
      void _progress_done();
      //---------------------------------------------------------------------------
      class STL3D                         // STL 3D mesh
          {                                                                      
      public:
          double center[3],size[3],rmax;  // bbox center,half sizes, max(size[])
          struct _fac
              {
              float p[3][3];              // triangle vertexes CCW order
              float n[3];                 // triangle unit normal pointing out
              WORD attr;
              _fac()  {}
              _fac(_fac& a)   { *this=a; }
              ~_fac() {}
              _fac* operator = (const _fac *a) { *this=*a; return this; }
              //_fac* operator = (const _fac &a) { ...copy... return this; }
              void compute()                                  // compute normal
                  {
                  float a[3],b[3];
                  vectorf_sub(a,p[1],p[0]);
                  vectorf_sub(b,p[2],p[1]);
                  vectorf_mul(n,a,b);
                  vectorf_one(n,n);
                  }
              double intersect_ray(double *pos,double *dir)   // return -1 or distance to triangle and unit ray intersection
                  {
                  double p0[3],p1[3],p2[3];                   // input triangle vertexes
                  double e1[3],e2[3],pp[3],qq[3],rr[3];       // dir must be unit vector !!!
                  double t,u,v,det,idet;
                  // get points
                  vector_ld(p0,p[0][0],p[0][1],p[0][2]);
                  vector_ld(p1,p[1][0],p[1][1],p[1][2]);
                  vector_ld(p2,p[2][0],p[2][1],p[2][2]);
                  //compute ray triangle intersection
                  vector_sub(e1,p1,p0);
                  vector_sub(e2,p2,p0);
                  // Calculate planes normal vector
                  vector_mul(pp,dir,e2);
                  det=vector_mul(e1,pp);
                  // Ray is parallel to plane
                  if (fabs(det)<1e-8) return -1.0;
                  idet=1.0/det;
                  vector_sub(rr,pos,p0);
                  u=vector_mul(rr,pp)*idet;
                  if ((u<0.0)||(u>1.0)) return -1.0;
                  vector_mul(qq,rr,e1);
                  v=vector_mul(dir,qq)*idet;
                  if ((v<0.0)||(u+v>1.0)) return -1.0;
                  // distance
                  t=vector_mul(e2,qq)*idet;
                  if (t<0.0) t=-1.0;
                  return t;
                  }
              };
          List<_fac> fac;                         // faces
      
          STL3D() { reset(); }
          STL3D(STL3D& a) { *this=a; }
          ~STL3D() {}
          STL3D* operator = (const STL3D *a) { *this=*a; return this; }
          //STL3D* operator = (const STL3D &a) { ...copy... return this; }
      
          void reset(){ fac.num=0; compute(); }   // clear STL
          void draw();                            // render STL mesh (OpenGL)
          void draw_normals(float size);          // render STL normals (OpenGL)
          void compute();                         // compute bbox
          void compute_normals();                 // recompute normals from points
          void supports(reper &obj);              // compute supports with obj placement above base plane z=0
          void load(AnsiString name);
          void save(AnsiString name);
          };
      //---------------------------------------------------------------------------
      void STL3D::draw()
          {
          _fac *f; int i,j; BYTE r,g,b;
          glBegin(GL_TRIANGLES);
          for (f=fac.dat,i=0;i<fac.num;i++,f++)
              {
              glNormal3fv(f->n);
              if (f->attr<32768)
                  {
                  r= f->attr     &31; r<<=3;
                  g=(f->attr>> 5)&31; g<<=3;
                  b=(f->attr>>10)&31; b<<=3;
                  glColor3ub(r,g,b);
                  }
              for (j=0;j<3;j++) glVertex3fv(f->p[j]);
              }
          glEnd();
          }
      //---------------------------------------------------------------------------
      void STL3D::draw_normals(float size)
          {
          _fac *f;
          int i; float a[3],b[3];
          glBegin(GL_LINES);
          for (f=fac.dat,i=0;i<fac.num;i++,f++)
              {
              vectorf_add(a,f->p[0],f->p[1]);
              vectorf_add(a,a      ,f->p[2]);
              vectorf_mul(a,a,1.0/3.0);
              vectorf_mul(b,f->n,size); glVertex3fv(a);
              vectorf_add(b,b,a);       glVertex3fv(b);
              }
          glEnd();
          }
      //---------------------------------------------------------------------------
      void STL3D::compute()
          {
          _fac *f;
          int i,j,k;
          double p0[3],p1[3];
          vector_ld(center,0.0,0.0,0.0);
          vector_ld(size,0.0,0.0,0.0);
          rmax=0.0;
          if (fac.num==0) return;
          // bbox
          for (k=0;k<3;k++) p0[k]=fac.dat[0].p[0][k];
          for (k=0;k<3;k++) p1[k]=fac.dat[0].p[0][k];
          for (f=fac.dat,i=0;i<fac.num;i++,f++)
           for (j=0;j<3;j++)
            for (k=0;k<3;k++)
              {
              if (p0[k]>f->p[j][k]) p0[k]=f->p[j][k];
              if (p1[k]<f->p[j][k]) p1[k]=f->p[j][k];
              }
          vector_add(center,p0,p1); vector_mul(center,center,0.5);
          vector_sub(size  ,p1,p0); vector_mul(size  ,size  ,0.5);
                            rmax=size[0];
          if (rmax<size[1]) rmax=size[1];
          if (rmax<size[2]) rmax=size[2];
          // attr repair
          for (f=fac.dat,i=0;i<fac.num;i++,f++)
           if (f->attr==0) f->attr=32768;
          }
      //---------------------------------------------------------------------------
      void STL3D::compute_normals()
          {
          _fac *f; int i;
          for (f=fac.dat,i=0;i<fac.num;i++,f++) f->compute();
          }
      //---------------------------------------------------------------------------
      void STL3D::supports(reper &obj)
          {
          _fac *f,ff;
          int i,j,k;
          double p[3],dp[3],x0,y0,h0,x1,y1,x2,y2,h1,t;
          // some config values first
          const WORD   attr0=31<<10;              // support attr should be different than joint
          const WORD   attr1=31<<5;               // joint attr should be different than mesh,support
          const double grid0=8.0;                 // distance between supports
          const double grid1=2.0;                 // distance between joints
          const double gap=grid0/tan(45.0*deg);// distance between main support and mesh (joint size)
          const double ha=1.0;                    // main support side size
          // do not mess with these
          const double hx=    ha*cos(60.0*deg);   // half size of main support in x
          const double hy=0.5*ha*sin(60.0*deg);   // half size of main support in y
          const double grid2=0.4*hy;              // distance between joints bases
          const double ga=2.0*grid2*grid1/grid0;  // main support side size
          const double gx=hx*grid2/grid0;         // half size of joint support in x
          const double gy=hy*grid2/grid0;         // half size of joint support in y
      
          // apply placement obj (may lose some accuracy) not needed if matrices are not used
          for (f=fac.dat,i=0;i<fac.num;i++,f++)
              {
              for (j=0;j<3;j++)
                  {
                  for (k=0;k<3;k++) p[k]=f->p[j][k];  // float->double
                  obj.l2g(p,p);
                  for (k=0;k<3;k++) f->p[j][k]=p[k];  // double->float
                  }
              for (k=0;k<3;k++) p[k]=f->n[k]; // float->double
              obj.l2g_dir(p,p);
              for (k=0;k<3;k++) f->n[k]=p[k]; // double->float
              } compute();
      
          // create supports
          for (x0=center[0]-size[0]+(0.5*grid0);x0<=center[0]+size[0]-(0.5*grid0);x0+=grid0)
           for (y0=center[1]-size[1]+(0.5*grid0);y0<=center[1]+size[1]-(0.5*grid0);y0+=grid0)
              {
              // cast ray x0,y0,0 in Z+ direction to check for mesh intersection to compute the support height h0
              h0=center[2]+size[2]+1e6;
              vector_ld(p,x0,y0,0.0);
              vector_ld(dp,0.0,0.0,+1.0);
              for (f=fac.dat,i=0;i<fac.num;i++,f++)
                  {
                  t=f->intersect_ray(p,dp);
                  if ((t>=0.0)&&(t<h0)) h0=t;
                  }
              if (h0>center[2]+size[2]+1e5) continue; // skip non intersected rays
              h0-=gap; if (h0<0.0) h0=0.0;
              // main suport prism
              ff.attr=attr0;
              // sides
              ff.attr=attr0;
              vectorf_ld(ff.p[0],x0-hx,y0-hy,0.0);
              vectorf_ld(ff.p[1],x0+hx,y0-hy,0.0);
              vectorf_ld(ff.p[2],x0-hx,y0-hy, h0); ff.compute(); fac.add(ff);
              vectorf_ld(ff.p[0],x0+hx,y0-hy,0.0);
              vectorf_ld(ff.p[1],x0+hx,y0-hy, h0);
              vectorf_ld(ff.p[2],x0-hx,y0-hy, h0); ff.compute(); fac.add(ff);
      
              vectorf_ld(ff.p[0],x0-hx,y0-hy, h0);
              vectorf_ld(ff.p[1],x0   ,y0+hy,0.0);
              vectorf_ld(ff.p[2],x0-hx,y0-hy,0.0); ff.compute(); fac.add(ff);
              vectorf_ld(ff.p[0],x0-hx,y0-hy, h0);
              vectorf_ld(ff.p[1],x0   ,y0+hy, h0);
              vectorf_ld(ff.p[2],x0   ,y0+hy,0.0); ff.compute(); fac.add(ff);
      
              vectorf_ld(ff.p[0],x0   ,y0+hy, h0);
              vectorf_ld(ff.p[1],x0+hx,y0-hy,0.0);
              vectorf_ld(ff.p[2],x0   ,y0+hy,0.0); ff.compute(); fac.add(ff);
              vectorf_ld(ff.p[0],x0   ,y0+hy, h0);
              vectorf_ld(ff.p[1],x0+hx,y0-hy, h0);
              vectorf_ld(ff.p[2],x0+hx,y0-hy,0.0); ff.compute(); fac.add(ff);
              // base triangles
              vectorf_ld(ff.p[0],x0   ,y0+hy,0.0);
              vectorf_ld(ff.p[1],x0+hx,y0-hy,0.0);
              vectorf_ld(ff.p[2],x0-hx,y0-hy,0.0); ff.compute(); fac.add(ff);
              vectorf_ld(ff.p[0],x0-hx,y0-hy, h0);
              vectorf_ld(ff.p[1],x0+hx,y0-hy, h0);
              vectorf_ld(ff.p[2],x0   ,y0+hy, h0); ff.compute(); fac.add(ff);
      
              // joints
              for (x1=x0-(0.5*grid0),x2=x0-(0.5*grid2);x1<=x0+(0.5*grid0);x1+=grid1,x2+=ga)
               for (y1=y0-(0.5*grid0),y2=y0-(1.9*grid2);y1<=y0+(0.5*grid0);y1+=grid1,y2+=ga)
                  {
                  // cast ray x1,y1,0 in Z+ direction to check for mesh intersection to compute the joint height h1
                  h1=h0+gap+1e6;
                  vector_ld(p,x1,y1,0.0);
                  vector_ld(dp,0.0,0.0,+1.0);
                  for (f=fac.dat,i=0;i<fac.num;i++,f++)
                      {
                      t=f->intersect_ray(p,dp);
                      if ((t>=0.0)&&(t<h1)) h1=t;
                      }
                  if (h1>h0+gap+1e5) continue; // skip non intersected rays
                  // tetrahedron joints
                  ff.attr=attr1;
                  // base triangle
                  vectorf_ld(ff.p[0],x2   ,y2+gy,h0);
                  vectorf_ld(ff.p[1],x2+gx,y2-gy,h0);
                  vectorf_ld(ff.p[2],x2-gx,y2-gy,h0); ff.compute(); fac.add(ff);
                  // sides
                  vectorf_ld(ff.p[0],x2+gx,y2-gy,h0);
                  vectorf_ld(ff.p[1],x2   ,y2+gy,h0);
                  vectorf_ld(ff.p[2],x1   ,y1   ,h1); ff.compute(); fac.add(ff);
                  vectorf_ld(ff.p[0],x2   ,y2+gy,h0);
                  vectorf_ld(ff.p[1],x2-gx,y2-gy,h0);
                  vectorf_ld(ff.p[2],x1   ,y1   ,h1); ff.compute(); fac.add(ff);
                  vectorf_ld(ff.p[0],x2+gx,y2+gy,h0);
                  vectorf_ld(ff.p[1],x2-gx,y2-gy,h0);
                  vectorf_ld(ff.p[2],x1   ,y1   ,h1); ff.compute(); fac.add(ff);
                  }
              }
      
          // reverse placement obj (may lose some accuracy) not needed if matrices are not used
          for (f=fac.dat,i=0;i<fac.num;i++,f++)
              {
              for (j=0;j<3;j++)
                  {
                  for (k=0;k<3;k++) p[k]=f->p[j][k];  // float->double
                  obj.g2l(p,p);
                  for (k=0;k<3;k++) f->p[j][k]=p[k];  // double->float
                  }
              for (k=0;k<3;k++) p[k]=f->n[k]; // float->double
              obj.g2l_dir(p,p);
              for (k=0;k<3;k++) f->n[k]=p[k]; // double->float
              } compute();
          }
      //---------------------------------------------------------------------------
      void STL3D::load(AnsiString name)
          {
          int   adr,siz,hnd;
          BYTE *dat;
          AnsiString lin,s;
          int i,j,l,n;
          _fac f;
      
          reset(); f.attr=0;
          siz=0;
          hnd=FileOpen(name,fmOpenRead);
          if (hnd<0) return;
          siz=FileSeek(hnd,0,2);
              FileSeek(hnd,0,0);
          dat=new BYTE[siz];
          if (dat==NULL) { FileClose(hnd); return; }
          FileRead(hnd,dat,siz);
          FileClose(hnd);
      
          adr=0; s=txt_load_str(dat,siz,adr,true);
          // ASCII
          if (s=="solid")
              {
              _progress_init(siz); int progress_cnt=0;
              for (adr=0;adr<siz;)
                  {
                  progress_cnt++; if (progress_cnt>=128) { progress_cnt=0; _progress(adr); }
                  lin=txt_load_lin(dat,siz,adr,true);
                  for (i=1,l=lin.Length();i<=l;)
                      {
                      s=str_load_str(lin,i,true);
                      if (s=="solid") { name=str_load_str(lin,i,true); break; }
                      if (s=="endsolid") break;
                      if (s=="facet")
                          {
                          j=0;
                          s=str_load_str(lin,i,true);
                          f.n[0]=str2num(str_load_str(lin,i,true));
                          f.n[1]=str2num(str_load_str(lin,i,true));
                          f.n[2]=str2num(str_load_str(lin,i,true));
                          }
                      if (s=="vertex")
                       if (j<3)
                          {
                          f.p[j][0]=str2num(str_load_str(lin,i,true));
                          f.p[j][1]=str2num(str_load_str(lin,i,true));
                          f.p[j][2]=str2num(str_load_str(lin,i,true));
                          j++;
                          if (j==3) fac.add(f);
                          }
                      break;
                      }
                  }
              }
          // binary
          else{
              adr=80;
              n=((DWORD*)(dat+adr))[0]; adr+=4;
              fac.allocate(n); fac.num=0;
              _progress_init(n); int progress_cnt=0;
              for (i=0;i<n;i++)
                  {
                  if (adr+50>siz) break;  // error
                  progress_cnt++; if (progress_cnt>=128) { progress_cnt=0; _progress(i); }
                  f.n[0]=((float*)(dat+adr))[0]; adr+=4;
                  f.n[1]=((float*)(dat+adr))[0]; adr+=4;
                  f.n[2]=((float*)(dat+adr))[0]; adr+=4;
                  for (j=0;j<3;j++)
                      {
                      f.p[j][0]=((float*)(dat+adr))[0]; adr+=4;
                      f.p[j][1]=((float*)(dat+adr))[0]; adr+=4;
                      f.p[j][2]=((float*)(dat+adr))[0]; adr+=4;
                      }
                  f.attr=((WORD*)(dat+adr))[0]; adr+=2;   // attributes
                  fac.add(f);
                  }
              }
          _progress_done();
          delete[] dat;
          compute();
          }
      //---------------------------------------------------------------------------
      void STL3D::save(AnsiString name)
          {
          // ToDo
          }
      //---------------------------------------------------------------------------
      void _progress_init(int n)
          {
          #ifdef ComctrlsHPP
          if (progress==NULL) return;
          progress->Position=0;
          progress->Max=n;
          progress->Visible=true;
          #endif
          }
      //---------------------------------------------------------------------------
      void _progress     (int ix)
          {
          #ifdef ComctrlsHPP
          if (progress==NULL) return;
          progress->Position=ix;
          progress->Update();
          #endif
          }
      //---------------------------------------------------------------------------
      void _progress_done()
          {
          #ifdef ComctrlsHPP
          if (progress==NULL) return;
          progress->Visible=false;
          #endif
          }
      //---------------------------------------------------------------------------
      #endif
      //---------------------------------------------------------------------------
      

      Usage is simple:

      #include "STL3D.h"                  // STL mesh (this is the important stuff)
      STL3D mesh;                         // point cloud and tetrahedronal mesh
      
      mesh.load("space_invader_magnet.stl");
      mesh.supports(obj); //  obj is object holding 4x4 uniform matrix of placement if you STL is already placed than it is not needed
      

      I used a lot of stuff from mine OpenGL engine like dynamic List<> template:


      List<double> xxx; is the same as double xxx[];
      xxx.add(5); adds 5 to end of the list
      xxx[7] access array element (safe)
      xxx.dat[7] access array element (unsafe but fast direct access)
      xxx.num is the actual used size of the array
      xxx.reset() clears the array and set xxx.num=0
      xxx.allocate(100) preallocate space for 100 items

      or vector and matrix math (vectorf_ works with float* and vector_ with double) which is not too important. If you need the math see:

      If the STL is already placed (no matrix) than no placement conversions nor the obj is needed at all. The code reflects the bullets above. I wanted to keep it as simple as I could so no optimizations are there yet.

      The gap and grid constants are hard-coded in the supports function and are not yet set to the valid values.

      [Notes]

      Now this barely cover only the very basic of the problem and there are a lot of edge cases left un-handled to keep this "short". The code itself does not check if triangles are above the 45 degree slope but that can be done with simple normal angle checking like:

      if (acos(dot(normal,(0.0,0.0,1.0))<45.0*deg) continue;
      

      There is the need to add also the supports between parts of mesh for example if your object has more layers than only the first layer will be supported from base plane. the rest must use the layer below itself ... and using weakened joint on both sides of support. That is similar to place the first layer of supports you just need to cast the ray in both directions ... or cast continuous ray going through the whole bbox and check for start/end surfaces by analyzing normal direction to ray (simple sign of dot product). For example this is mesh placement that would possibly need this (for some technologies):

      While designing the supports take in mind you should meet the proper winding rule (CCW) and normal direction (out) for the printing process ...

      这篇关于将外部支持者生成为用于 3D 打印的网格的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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