将外部支持者生成网格以进行 3D 打印

序幕

这是我尝试重新询问已关闭的

我们需要添加尽可能少的材料,并且仍然具有足够的强度以将我们的网格固定在适当的位置而不会弯曲.最重要的是,我们需要削弱网格附近的支撑,以便在打印后轻松折断.

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

问题:

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

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

此形状将从某个基本平面 (Z=0) 开始,然后向上直到碰到网格.然而,为了完成这项工作,支撑必须在网格和自身之间有一个小的 gap,我们将在其中添加我们的弱化关节结构和网格.

  • 支持模式

    这里有很多选择,所以我选择了最简单的(但不是防犯规),即把支撑放置在一个统一的网格中,支撑之间的距离恒定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 文件加载进度条#万一无效_progress_init(int n);无效_progress(int ix);无效_progress_done();//--------------------------------------------------------------------------class STL3D//STL 3D 网格{上市:双中心[3],尺寸[3],rmax;//bbox center,half size, 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()//计算正常{浮动一个[3],b[3];矢量f_sub(a,p[1],p[0]);矢量f_sub(b,p[2],p[1]);矢量f_mul(n,a,b);矢量f_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]);矢量_ld(p2,p[2][0],p[2][1],p[2][2]);//计算射线三角形交点向量子(e1,p1,p0);vector_sub(e2,p2,p0);//计算平面法线向量vector_mul(pp,dir,e2);det=vector_mul(e1,pp);//射线平行于平面if (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;}无效重置(){ fac.num = 0;计算();}//清除 STL无效绘制();//渲染 STL 网格(OpenGL)无效draw_normals(浮动大小);//渲染 STL 法线 (OpenGL)无效计算();//计算bbox无效计算正常();//从点重新计算法线无效支持(reper &obj);//计算 obj 放置在基本平面 z=0 上方的支撑无效负载(AnsiString 名称);无效保存(AnsiString 名称);};//--------------------------------------------------------------------------无效 STL3D::draw(){_fac *f;整数 i, j;字节 r,g,b;glBegin(GL_TRIANGLES);对于 (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;诠释我;浮动一个[3],b[3];glBegin(GL_LINES);对于 (f=fac.dat,i=0;ip[0],f->p[1]);矢量f_add(a,a,f->p[2]);矢量f_mul(a,a,1.0/3.0);矢量f_mul(b,f->n,size);glVertex3fv(a);矢量f_add(b,b,a);glVertex3fv(b);}glEnd();}//--------------------------------------------------------------------------无效 STL3D::compute(){_fac *f;整数 i,j,k;双 p0[3],p1[3];vector_ld(中心,0.0,0.0,0.0);vector_ld(大小,0.0,0.0,0.0);rmax=0.0;如果(fac.num==0)返回;//bbox对于 (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];对于 (f=fac.dat,i=0;ip[j][k]) p1[k]=f->p[j][k];}矢量添加(中心,p0,p1);矢量_mul(中心,中心,0.5);向量子(大小,p1,p0);vector_mul(大小,大小,0.5);rmax=大小[0];if (rmaxattr==0) f->attr=32768;}//--------------------------------------------------------------------------无效 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;//支持 attr 应该不同于jointconst WORD attr1=31<<5;//关节属性应该不同于网格,支持常量双网格0=8.0;//支撑之间的距离常量双网格1=2.0;//关节之间的距离const double gap=grid0/tan(45.0*deg);//主支撑与网格之间的距离(接头尺寸)常量双 ha=1.0;//主支撑边尺寸//不要乱用这些const double hx= ha*cos(60.0*deg);//x 中主支撑的一半大小const double hy=0.5*ha*sin(60.0*deg);//y 中主支撑的一半大小常量双网格2=0.4*hy;//关节基础之间的距离常量双 ga=2.0*grid2*grid1/grid0;//主支撑边尺寸常量双 gx=hx*grid2/grid0;//x 中关节支撑的一半大小常量双 gy=hy*grid2/grid0;//y 中关节支撑的一半大小//如果不使用矩阵,则不需要应用放置 obj(可能会失去一些准确性)对于 (f=fac.dat,i=0;ip[j][k];//float->doubleobj.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];//float->doubleobj.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);对于 (f=fac.dat,i=0;iintersect_ray(p,dp);if ((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;矢量f_ld(ff.p[0],x0-hx,y0-hy,0.0);矢量f_ld(ff.p[1],x0+hx,y0-hy,0.0);矢量f_ld(ff.p[2],x0-hx,y0-hy, h0);ff.计算();fac.add(ff);矢量f_ld(ff.p[0],x0+hx,y0-hy,0.0);矢量f_ld(ff.p[1],x0+hx,y0-hy,h0);矢量f_ld(ff.p[2],x0-hx,y0-hy, h0);ff.计算();fac.add(ff);矢量f_ld(ff.p[0],x0-hx,y0-hy, h0);矢量f_ld(ff.p[1],x0,y0+hy,0.0);矢量f_ld(ff.p[2],x0-hx,y0-hy,0.0);ff.计算();fac.add(ff);矢量f_ld(ff.p[0],x0-hx,y0-hy, h0);矢量f_ld(ff.p[1],x0,y0+hy,h0);矢量f_ld(ff.p[2],x0,y0+hy,0.0);ff.计算();fac.add(ff);矢量f_ld(ff.p[0],x0,y0+hy,h0);矢量f_ld(ff.p[1],x0+hx,y0-hy,0.0);矢量f_ld(ff.p[2],x0,y0+hy,0.0);ff.计算();fac.add(ff);矢量f_ld(ff.p[0],x0,y0+hy,h0);矢量f_ld(ff.p[1],x0+hx,y0-hy,h0);矢量f_ld(ff.p[2],x0+hx,y0-hy,0.0);ff.计算();fac.add(ff);//基础三角形矢量f_ld(ff.p[0],x0,y0+hy,0.0);矢量f_ld(ff.p[1],x0+hx,y0-hy,0.0);矢量f_ld(ff.p[2],x0-hx,y0-hy,0.0);ff.计算();fac.add(ff);矢量f_ld(ff.p[0],x0-hx,y0-hy, h0);矢量f_ld(ff.p[1],x0+hx,y0-hy,h0);矢量f_ld(ff.p[2],x0,y0+hy,h0);ff.计算();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);对于 (f=fac.dat,i=0;iintersect_ray(p,dp);如果 ((t>=0.0)&&(th0+gap+1e5) 继续;//跳过不相交的光线//四面体关节ff.attr=attr1;//基础三角形矢量f_ld(ff.p[0],x2,y2+gy,h0);矢量f_ld(ff.p[1],x2+gx,y2-gy,h0);矢量f_ld(ff.p[2],x2-gx,y2-gy,h0);ff.计算();fac.add(ff);//边矢量f_ld(ff.p[0],x2+gx,y2-gy,h0);矢量f_ld(ff.p[1],x2,y2+gy,h0);矢量f_ld(ff.p[2],x1,y1,h1);ff.计算();fac.add(ff);矢量f_ld(ff.p[0],x2,y2+gy,h0);矢量f_ld(ff.p[1],x2-gx,y2-gy,h0);矢量f_ld(ff.p[2],x1,y1,h1);ff.计算();fac.add(ff);矢量f_ld(ff.p[0],x2+gx,y2+gy,h0);矢量f_ld(ff.p[1],x2-gx,y2-gy,h0);矢量f_ld(ff.p[2],x1,y1,h1);ff.计算();fac.add(ff);}}//如果不使用矩阵,则不需要反向放置 obj(可能会失去一些准确性)对于 (f=fac.dat,i=0;ip[j][k];//float->doubleobj.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];//float->doubleobj.g2l_dir(p,p);对于 (k=0;k<3;k++) f->n[k]=p[k];//double->float} 计算();}//--------------------------------------------------------------------------无效 STL3D::load(AnsiString 名称){int adr,大小,hnd;字节 *dat;AnsiString lin,s;整数 i,j,l,n;_fac f;重启();f.attr=0;大小=0;hnd=FileOpen(名称,fmOpenRead);如果 (hnd<0) 返回;siz=FileSeek(hnd,0,2);FileSeek(hnd,0,0);dat=新字节[大小];if (dat==NULL) { FileClose(hnd);返回;}文件读取(hnd,dat,siz);文件关闭(hnd);adr=0;s=txt_load_str(dat,siz,adr,true);//ASCII如果 (s=="固体"){_progress_init(siz);int 进度_cnt=0;对于 (adr=0;adr=128){progress_cnt=0;_progress(adr);}lin=txt_load_lin(dat,siz,adr,true);对于 (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") 中断;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));}如果 (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++;如果 (j==3) fac.add(f);}休息;}}}//二进制别的{adr=80;n=((DWORD*)(dat+adr))[0];adr+=4;fac.allocate(n);fac.num=0;_progress_init(n);int 进度_cnt=0;对于 (i=0;isiz) 中断;//错误进度_cnt++;如果(progress_cnt>=128){progress_cnt=0;_进度(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();删除[]数据;计算();}//--------------------------------------------------------------------------无效 STL3D::save(AnsiString 名称){//去做}//--------------------------------------------------------------------------无效_progress_init(int n){#ifdef ComctrlsHPP如果(进度==NULL)返回;进度->位置=0;进度->Max=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<> 模板:


    列表<double>xxx; 等同于 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:

      • Supports in 3D Printing: A technology overview

      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:

      • Understanding 4x4 homogenous transform matrices

      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 ...

    相关文章