# 3D data set to stereo lithography input

jtigor

Mon, 07/16/2012 - 06:24 am

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