3D data set to stereo lithography input
jtigor
I need to get data describing a 3D surface (from AFM data... it's just z values sampled over a uniform xy grid) to a stereo lithography machine. Has anyone tackled this before?
Apparently, an stl file is a standard input file for these devices. This file describes the surface via a mesh of triangles along with the normal to each triangle. The file then consists of an xyz triplet for each vertex of each triangle along with the normal. I haven't found anything (yet) in Igor to create the mesh. Image transform seems to be the likely suspect, but this hasn't panned out, unless I'm missing something.
I'm hopeful this post will generate some ideas.
Thanks,
Jeff
ImageInterpolate
operation with the Voronoi method. This requires an x,y,z triplet source wave, so you will have to convert your gridded data to a big triplet wave. Then use the /STW flag to save the Delauney trangulation data.July 16, 2012 at 06:59 am - Permalink
Indeed ImageTransform has a well-hidden feature called ccsubdivision. However, the Catmull-Clark subdivision is probably more appropriate for Pixar-type applications then for what you are doing. It is designed to create a higher density mesh so you can describe smoother surfaces.
If your original data represent samples on a regular rectangular grid then the actual triangulation is trivial, though non-unique as for each rectangle there are two choices of triangle representation. IP7 Gizmo does this calculation internally, i.e., for each rectangle create two triangles and compute the normals. The calculation of the normals is straightforward: pick a vertex of the triangle and compute the cross-product of the two vectors from the selected vertex to the other two triangle vertices.
Feel free to contact me directly through support@wavemetrics.com and I'll help you with that.
A.G.
WaveMetrics, Inc.
July 16, 2012 at 11:17 am - Permalink
AG -- I will follow up privately and post my results later.
Thanks,
Jeff
July 16, 2012 at 12:45 pm - Permalink
I thought it was obvious that Steve is correct and that you would get the triangulation for your data using ImageInterpolate Voronoi.
The down-side of this approach is that you are taking data that are essentially already triangulated and you run a very complex calculation, O(N^2), just to obtain a triangulation which you already know. Also, regardless of how you obtain the triangulation, you still need to generate the normals in a consistent manner. The latter, in my view, is the more interesting part of the problem.
AG
July 16, 2012 at 01:47 pm - Permalink
The output of ImageInterpolate for Voronoi with the /STW flag was a single column wave (W_TriangulationData). Are the components of each vertex on sequential rows in this wave?
At this point, it's all interesting for me.
Thanks,
Jeff
July 16, 2012 at 02:46 pm - Permalink
If you just want the "edges" you can use the /SV flag. The /STW flag results in a wave that is designed to be used internally; it is intentionally undocumented as it contains information that is used to reconstruct the internal structures that are built during the triangulation process; it does not have direct vertex information.
Below I have pasted complete code for generating the triangles and their normals for a 2D matrix wave. This is not necessarily a very efficient way of computing this but it should get the job done.
I hope this helps,
AG
Wave inWave
Variable rows=DimSize(inWave,0)
Variable cols=DimSize(inWave,1)
Variable numTriangles=2*(rows-1)*(cols-1)
if(numTriangles<=0)
doAlert 0, "What am I doing here"
return 0
endif
Make/O/N=(numTriangles*3,3) triangleWave
Variable triangleLineIndex=0
Variable xx,yy,zz,i,j
for(i=1;i<rows;i+=1)
for(j=1;j<cols;j+=1)
getTopTriangle(inWave,i,j,triangleWave,triangleLineIndex)
triangleLineIndex+=3
getBottomTriangle(inWave,i,j,triangleWave,triangleLineIndex)
triangleLineIndex+=3
endfor
endfor
Make/O/N=(numTriangles,3) normalsWave
Make/FREE/D/N=(3) nv
Variable d,x1,y1,z1,x2,y2,z2
triangleLineIndex=0
for(i=0;i<numTriangles;i+=1)
x1=triangleWave[triangleLineIndex+1][0]-triangleWave[triangleLineIndex][0]
y1=triangleWave[triangleLineIndex+1][1]-triangleWave[triangleLineIndex][1]
z1=triangleWave[triangleLineIndex+1][2]-triangleWave[triangleLineIndex][2]
x2=triangleWave[triangleLineIndex][0]-triangleWave[triangleLineIndex+2][0]
y2=triangleWave[triangleLineIndex][1]-triangleWave[triangleLineIndex+2][1]
z2=triangleWave[triangleLineIndex][2]-triangleWave[triangleLineIndex+2][2]
triangleLineIndex+=3
nv[0]=y1*z2-z1*y2
nv[1]=z1*x2-x1*z2
nv[2]=x1*y2-y1*x2
d=norm(nv)
if(d<=0)
nv[2]=1 // should not happen
else
nv[0]/=d
nv[1]/=d
nv[2]/=d
endif
normalsWave[i][]=nv[q]
endfor
End
Function getTopTriangle(inWave,i,j,triangleWave,triangleLineIndex)
Wave inWave,triangleWave
Variable i,j,triangleLineIndex
// the upper triangle consists of (i,j-1),(i-1,j),(i-1,j-1)
triangleWave[triangleLineIndex][0]=DimOffset(inWave,0)+i*DimDelta(inWave,0)
triangleWave[triangleLineIndex][1]=DimOffset(inWave,1)+(j-1)*DimDelta(inWave,1)
triangleWave[triangleLineIndex][2]=inWave[i][j-1]
triangleLineIndex+=1
triangleWave[triangleLineIndex][0]=DimOffset(inWave,0)+(i-1)*DimDelta(inWave,0)
triangleWave[triangleLineIndex][1]=DimOffset(inWave,1)+j*DimDelta(inWave,1)
triangleWave[triangleLineIndex][2]=inWave[i-1][j]
triangleLineIndex+=1
triangleWave[triangleLineIndex][0]=DimOffset(inWave,0)+(i-1)*DimDelta(inWave,0)
triangleWave[triangleLineIndex][1]=DimOffset(inWave,1)+(j-1)*DimDelta(inWave,1)
triangleWave[triangleLineIndex][2]=inWave[i-1][j-1]
End
Function getBottomTriangle(inWave,i,j,triangleWave,triangleLineIndex)
Wave inWave,triangleWave
Variable i,j,triangleLineIndex
// the lower triangle consists of (i,j-1),(i,j),(i-1,j)
triangleWave[triangleLineIndex][0]=DimOffset(inWave,0)+i*DimDelta(inWave,0)
triangleWave[triangleLineIndex][1]=DimOffset(inWave,1)+(j-1)*DimDelta(inWave,1)
triangleWave[triangleLineIndex][2]=inWave[i][j-1]
triangleLineIndex+=1
triangleWave[triangleLineIndex][0]=DimOffset(inWave,0)+i*DimDelta(inWave,0)
triangleWave[triangleLineIndex][1]=DimOffset(inWave,1)+j*DimDelta(inWave,1)
triangleWave[triangleLineIndex][2]=inWave[i][j]
triangleLineIndex+=1
triangleWave[triangleLineIndex][0]=DimOffset(inWave,0)+(i-1)*DimDelta(inWave,0)
triangleWave[triangleLineIndex][1]=DimOffset(inWave,1)+j*DimDelta(inWave,1)
triangleWave[triangleLineIndex][2]=inWave[i-1][j]
End
July 17, 2012 at 10:32 am - Permalink
Sorry for taking so long to get back. Thank you for posting your code, it was very helpful. I do need the triangle mesh with normals, so being able to do the calculations is important. My surface is relatively simple in that it can be described by a 2D array; it is clear where the outside of the surface lies.
I have also been able to write files for the stereo lithography and have had models created. This has worked out very well.
Thanks, again,
Jeff
July 27, 2012 at 08:45 am - Permalink