将外部支持者生成为用于 3D 打印的网格 [英] Generating outside supporters into mesh for 3D printing
问题描述
序言
这是我试图重新询问封闭的
对于粗糙,我们需要添加尽可能少的材料,并且仍然足够坚固以将我们的网格固定到位而不会弯曲.最重要的是,我们需要削弱网格附近的支撑,以便在打印后轻松折断.
不要忘记形状和位置取决于许多因素,例如使用的材料和技术、热流.
问题:
为了将这个庞大的主题缩小为可回答的问题,让我们只关注这个问题:
如何合并 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<>
模板:
List
等同于 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.
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 smallgap
between mesh and itself where we will add our weakened joint structure with mesh latter on.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.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 covergrid
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 asdouble xxx[];
xxx.add(5);
adds5
to end of the listxxx[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 arrayxxx.reset()
clears the array and setxxx.num=0
xxx.allocate(100)
preallocate space for100
itemsor vector and matrix math (
vectorf_
works withfloat*
andvector_
withdouble
) 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
andgrid
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屋!