Impresión 3D con Sage

1079 days ago by jlbravo

Impresión 3D con Sage

Triángulos STL

La primera función que vamos a definir escribe un triángulo (tres vértices) en un archivo en formato STL. Lo único que hay que hacer es escribir el vector normal (producto vectorial de dos de las aristas) y los tres vértices. Las coordenadas de los vértices deben ser positivas, según la descripción del formato STL.

%auto def write_face_stl(p1,p2,p3,out,precision=10^-10): # Escribe un triángulo orientado en formato STL en el fichero abierto en out. if norm(vector(p1)-vector(p2))>precision and norm(vector(p3)-vector(p2))>precision and norm(vector(p1)-vector(p3))>precision: normal=(vector(RDF,p2)-vector(RDF,p1)).cross_product(vector(RDF,p3)-vector(RDF,p1)) out.write('facet normal '+str(normal[0])+' '+str(normal[1])+' '+str(normal[2])+' '+'\n') out.write(' outer loop '+'\n') out.write(' vertex '+str(RDF(p1[0]))+' '+str(RDF(p1[1]))+' '+str(RDF(p1[2]))+' '+'\n') out.write(' vertex '+str(RDF(p2[0]))+' '+str(RDF(p2[1]))+' '+str(RDF(p2[2]))+' '+'\n') out.write(' vertex '+str(RDF(p3[0]))+' '+str(RDF(p3[1]))+' '+str(RDF(p3[2]))+' '+'\n') out.write(' endloop '+'\n') out.write('endfacet '+'\n') 
       

Superficie definida por una función en STL

Superfice definida por una función

La siguiente función devuelve la triangulación de la superficie definida por la gráfica de una función de dos variables. Se puede elegir la orientación (es decir, cuál es la parte "de arriba" y "de abajo".

f(x,y)=(sin((x-1)*pi)*cos((y-1)*pi)+1)/4 
       
%auto def oriented_plot3d(f,vx,vy,orientation='positive'): # Escribe la superficie definida por f en los puntos vx x vy en el fichero out. triangle_list=[] n,m=len(vx),len(vy) # Superficie for i in range(1,n): for j in range(1,m): p1=(vx[i-1],vy[j-1],f(vx[i-1],vy[j-1])) p2=(vx[i],vy[j-1],f(vx[i],vy[j-1])) p3=(vx[i],vy[j],f(vx[i],vy[j])) p4=(vx[i-1],vy[j],f(vx[i-1],vy[j])) if orientation=='positive': triangle_list.append(polygon3d([p1,p2,p3])) triangle_list.append(polygon3d([p1,p3,p4])) else: triangle_list.append(polygon3d([p1,p3,p2])) triangle_list.append(polygon3d([p1,p4,p3])) return triangle_list 
       
sum(oriented_plot3d(f,[0,0.1..1],[0,0.1..1])) 
       
Sleeping...
If no image appears re-execute the cell. 3-D viewer has been updated.

Sección de una función

La siguiente función recibe una función de dos variables y una lista de puntos. Para cada punto p toma como coordenada z, 0 y f(p) y genera la superficie vertical que resulta al ir uniendo los puntos consecutivos.

%auto def oriented_section(f,px,orientation='positive'): # Input: a function of two variables and a list of points of the plane # Output: writes in the open file out a vertical surface from the list of points (with height zero) # to the image of these points. triangle_list=[] n=len(px) vx=map(lambda x:x[0],px) vy=map(lambda x:x[1],px) for i in range(1,n): p1=(vx[i-1],vy[i-1],f(vx[i-1],vy[i-1])) p2=(vx[i],vy[i],f(vx[i],vy[i])) p3=(vx[i],vy[i],0) p4=(vx[i-1],vy[i-1],0) if orientation=='positive': triangle_list.append(polygon3d([p1,p2,p3])) triangle_list.append(polygon3d([p1,p3,p4])) else: triangle_list.append(polygon3d([p1,p3,p2])) triangle_list.append(polygon3d([p1,p4,p3])) return triangle_list 
       
sum(oriented_section(f,[(k,k) for k in [0,0.1..1]])) 
       
Sleeping...
If no image appears re-execute the cell. 3-D viewer has been updated.

Construimos un sólido

La siguiente función genera un sólido en STL, definido como el volumen entre la gráfica de una función en la cuadrícula especificada y el plano $z=0$.

%auto def write_solid_stl(f,vx,vy,filename='surface'): # Input: a two variables function a two lists of coordinates to evaluate. # Output: a STL file of the solid defined between the graph of the function and the plane z=0 triangle_list=[] n,m=len(vx),len(vy) # Surface defined by the function surface=oriented_plot3d(f,vx,vy,orientation='positive') for u in surface: triangle_list.append(u) # Sides surface=oriented_section(f,zip(vx,[vy[0] for _ in vy]),orientation='negative') for u in surface: triangle_list.append(u) surface=oriented_section(f,zip(vx,[vy[m-1] for _ in vy]),orientation='positive') for u in surface: triangle_list.append(u) surface=oriented_section(f,zip([vx[0] for _ in vx],vy),orientation='positive') for u in surface: triangle_list.append(u) surface=oriented_section(f,zip([vx[m-1] for _ in vy],vy),orientation='negative') for u in surface: triangle_list.append(u) # Base base=oriented_plot3d(lambda x,y:0,vx,vy,orientation='negative') for u in base: triangle_list.append(u) # We generate the STL file by writing all faces in triangle_list out = file(DATA+filename+'.stl','w') out.write('solid ' + filename+ '\n') for t in triangle_list: v=t.vertex_list() write_face_stl(v[0],v[1],v[2],out) out.write('endsolid prueba '+'\n') out.close() return sum(triangle_list) 
       

Para usar la función anterior, definimos una función de dos variables (estrictamente positiva), una lista de valores de $x$ en los que evaluará otra para los valores de $y$. Evalua la función en el producto cartesiano de las dos listas. Devuelve una superficie y guarda el archivo STL en DATA.

write_solid_stl(f,[0,0.1..1],[0,0.1..1]) 
       
Sleeping...
If no image appears re-execute the cell. 3-D viewer has been updated.

Superficie implícita

Vamos a tratar de dibujar la superficie definida por una ecuación del tipo $f(x,y,z)=0$

%auto def write_graph3d_stl(g,filename='surface'): # Input: A (closed) surface g # A function g_orientation such that for each point p of the surface, returns the orientation # The filename to save the STL solid. # Output: A file with the surface in STL format out = file(DATA+filename+'.stl','w') out.write('solid ' + filename+ '\n') g.triangulate() for f in g.faces(): write_face_stl(f[0],f[1],f[2],out) out.write('endsolid prueba '+'\n') out.close() 
       
var('x,y,z') f(x,y,z)=x^2-y^4/5+y^2+z^2-z^4/6-1 g=implicit_plot3d(f(x,y,z)==0.1,(x,-2,2),(y,-1.4,1.4),(z,-2,2)) g 
       
Sleeping...
If no image appears re-execute the cell. 3-D viewer has been updated.
write_graph3d_stl(g,filename='rugby') 
       
cc = 2; m = 13; pp = 19; f(x,y,z)=((x^2 + y^2 - cc^2)^2 + (z - 1)^2*(z + 1)^2)*((y^2 + z^2 - cc^2)^2 + (x - 1)^2*(x + 1)^2)*((z^2 + x^2 - cc^2)^2 + (y - 1)^2*(y + 1)^2)-3 g=implicit_plot3d(f(x,y,z),(x,-2.6,2.6),(y,-2.6,2.6),(z,-2.6,2.6), plot_points=100) g 
       
Sleeping...
If no image appears re-execute the cell. 3-D viewer has been updated.
write_graph3d_stl(g,filename='bola_con_agujeros') 
       

Superficies paramétricas

Vamos a generar una triangulación de una superficie dada por ecuaciones paramétricas.

%auto def write_parametric_STL(f,vx,vy,filename='parametric',orientation='positive',precision=10^-10): # Escribe la superficie definida por f en los puntos vx x vy en el fichero out. triangle_list=[] n,m=len(vx),len(vy) # Superficie for i in range(1,n): for j in range(1,m): p1=f(vx[i-1],vy[j-1]) p2=f(vx[i],vy[j-1]) p3=f(vx[i],vy[j]) p4=f(vx[i-1],vy[j]) if orientation!='positive': triangle_list.append(polygon3d([p1,p2,p3])) triangle_list.append(polygon3d([p1,p3,p4])) else: triangle_list.append(polygon3d([p1,p3,p2])) triangle_list.append(polygon3d([p1,p4,p3])) # We generate the STL file by writing all faces in triangle_list out = file(DATA+filename+'.stl','w') out.write('solid ' + filename+ '\n') for t in triangle_list: v=t.vertex_list() write_face_stl(v[0],v[1],v[2],out) out.write('endsolid prueba '+'\n') out.close() return sum(triangle_list) 
       
f(u,v)=((2+cos(u)/2)*cos(v),(2+cos(u)/2)*sin(v),sin(u)/2) write_parametric_STL(f,[0,pi/20..(2*pi)],[0,pi/20..(2*pi)]).show(aspect_ratio=[1,1,1]) 
       
__main__:1: UserWarning: Duplicate name: 'obj_778599.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_285195.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_523714.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_653733.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_729584.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_458453.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_659510.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_475994.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_778599.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_285195.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_523714.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_653733.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_729584.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_458453.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_659510.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_475994.pmesh'
Sleeping...
If no image appears re-execute the cell. 3-D viewer has been updated.

Un ejemplo más complicado

camino(v)=(cos(2*v)+cos(5*v)/7,sin(2*v)+sin(5*v)/7,sin(3*v)/3) parametric_plot3d(camino(v),(v,0,2*pi)) 
       
Sleeping...
If no image appears re-execute the cell. 3-D viewer has been updated.

Calculamos una base ortonormal en cada punto del camino

def normalizado(v): suma=sum([k^2 for k in v]) return [k/sqrt(suma) for k in v] t=camino.diff() def base_ortogonal(v): tangente=vector(normalizado([ k[0] for k in t(v)])) n1=vector(normalizado([tangente[1],-tangente[0],0])) n2=n1.cross_product(tangente) return n1,n2 
       
seccion(u)=((1+sin(3*u)/3)*cos(u)/10,(1+sin(3*u)/3)*sin(u)/10) parametric_plot(seccion(u),(u,0,2*pi)) 
       
def f(u,v): n1,n2=base_ortogonal(v) return camino(v)+n1*seccion(u)[0]+n2*seccion(u)[1] g=write_parametric_STL(f,[0,pi/15..(2*pi)],[0,pi/50..(2*pi)],filename='trefoil') g.show(aspect_ratio=[1,1,1]) 
       
__main__:1: UserWarning: Duplicate name: 'obj_986832.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_186226.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_178490.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_211770.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_671003.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_157700.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_976796.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_872871.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_661818.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_986292.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_933014.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_610964.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_974193.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_118548.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_748773.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_304554.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_604610.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_638980.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_989582.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_986832.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_186226.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_178490.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_211770.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_671003.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_157700.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_976796.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_872871.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_661818.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_986292.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_933014.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_610964.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_974193.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_118548.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_748773.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_304554.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_604610.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_638980.pmesh'
__main__:1: UserWarning: Duplicate name: 'obj_989582.pmesh'
Sleeping...
If no image appears re-execute the cell. 3-D viewer has been updated.

Otros formatos

Se puede transformar el archivo STL en otros formatos de 3D, por ejemplo, con

http://www.greentoken.de/onlineconv/