@@ -205,6 +205,175 @@ function surface_nets(data::Vector{T}, dims,eps,scale,origin) where {T}
205205 vertices, faces # faces are quads, indexed to vertices
206206end
207207
208+ """
209+ Generate a mesh using naive surface nets.
210+ This takes the center of mass of the voxel as the vertex for each cube.
211+ """
212+ function surface_nets (f:: Function , dims:: NTuple{3,Int} ,eps,scale,origin)
213+
214+ # TODO
215+ T = Float64
216+
217+ vertices = Point{3 ,T}[]
218+ faces = Face{4 ,Int}[]
219+
220+ sizehint! (vertices,ceil (Int,maximum (dims)^ 2 / 2 ))
221+ sizehint! (faces,ceil (Int,maximum (dims)^ 2 / 2 ))
222+
223+ n = 0
224+ x = [0 ,0 ,0 ]
225+ R = Array {Int} ([1 , (dims[1 ]+ 1 ), (dims[1 ]+ 1 )* (dims[2 ]+ 1 )])
226+ buf_no = 1
227+
228+ buffer = fill (zero (Int),R[3 ]* 2 )
229+
230+ v = Vector {T} ([0.0 ,0.0 ,0.0 ])
231+
232+ # March over the voxel grid
233+ x[3 ] = 0
234+ @inbounds while x[3 ]< dims[3 ]- 1
235+
236+ # m is the pointer into the buffer we are going to use.
237+ # This is slightly obtuse because javascript does not have good support for packed data structures, so we must use typed arrays :(
238+ # The contents of the buffer will be the indices of the vertices on the previous x/y slice of the volume
239+ m = 1 + (dims[1 ]+ 1 ) * (1 + buf_no * (dims[2 ]+ 1 ))
240+
241+ x[2 ]= 0
242+ @inbounds while x[2 ]< dims[2 ]- 1
243+
244+ x[1 ]= 0
245+ @inbounds while x[1 ] < dims[1 ]- 1
246+
247+ # Read in 8 field values around this vertex and store them in an array
248+ points = (Point {3,Float64} (x[1 ],x[2 ],x[3 ]).* scale + origin,
249+ Point {3,Float64} (x[1 ]+ 1 ,x[2 ],x[3 ]).* scale + origin,
250+ Point {3,Float64} (x[1 ],x[2 ]+ 1 ,x[3 ]).* scale + origin,
251+ Point {3,Float64} (x[1 ]+ 1 ,x[2 ]+ 1 ,x[3 ]).* scale + origin,
252+ Point {3,Float64} (x[1 ],x[2 ],x[3 ]+ 1 ).* scale + origin,
253+ Point {3,Float64} (x[1 ]+ 1 ,x[2 ],x[3 ]+ 1 ).* scale + origin,
254+ Point {3,Float64} (x[1 ],x[2 ]+ 1 ,x[3 ]+ 1 ).* scale + origin,
255+ Point {3,Float64} (x[1 ]+ 1 ,x[2 ]+ 1 ,x[3 ]+ 1 ).* scale + origin)
256+
257+ @inbounds grid = map (f, points)
258+
259+ # Also calculate 8-bit mask, like in marching cubes, so we can speed up sign checks later
260+ mask = 0x00
261+ signbit (grid[1 ]) && (mask |= 0x01 )
262+ signbit (grid[2 ]) && (mask |= 0x02 )
263+ signbit (grid[3 ]) && (mask |= 0x04 )
264+ signbit (grid[4 ]) && (mask |= 0x08 )
265+ signbit (grid[5 ]) && (mask |= 0x10 )
266+ signbit (grid[6 ]) && (mask |= 0x20 )
267+ signbit (grid[7 ]) && (mask |= 0x40 )
268+ signbit (grid[8 ]) && (mask |= 0x80 )
269+
270+ # Check for early termination if cell does not intersect boundary
271+ if mask == 0x00 || mask == 0xff
272+ x[1 ] += 1
273+ n += 1
274+ m += 1
275+ continue
276+ end
277+
278+ # Sum up edge intersections
279+ edge_mask = sn_edge_table[mask+ 1 ]
280+ v[1 ] = 0.0
281+ v[2 ] = 0.0
282+ v[3 ] = 0.0
283+ e_count = 0
284+
285+ # For every edge of the cube...
286+ @inbounds for i= 0 : 11
287+
288+ # Use edge mask to check if it is crossed
289+ if (edge_mask & (1 << i)) == 0
290+ continue
291+ end
292+
293+ # If it did, increment number of edge crossings
294+ e_count += 1
295+
296+ # Now find the point of intersection
297+ e0 = cube_edges[(i<< 1 )+ 1 ] # Unpack vertices
298+ e1 = cube_edges[(i<< 1 )+ 2 ]
299+ g0 = grid[e0+ 1 ] # Unpack grid values
300+ g1 = grid[e1+ 1 ]
301+ t = g0 - g1 # Compute point of intersection
302+ if abs (t) > eps
303+ t = g0 / t
304+ else
305+ continue
306+ end
307+
308+ # Interpolate vertices and add up intersections (this can be done without multiplying)
309+ k = 1
310+ for j = 1 : 3
311+ a = e0 & k
312+ b = e1 & k
313+ (a != 0 ) && (v[j] += 1.0 )
314+ if a != b
315+ v[j] += (a != 0 ? - t : t)
316+ end
317+ k<<= 1
318+ end
319+ end # edge check
320+
321+ # Now we just average the edge intersections and add them to coordinate
322+ s = 1.0 / e_count
323+ for i= 1 : 3
324+ @inbounds v[i] = (x[i] + s * v[i])# * scale[i] + origin[i]
325+ end
326+
327+ # Add vertex to buffer, store pointer to vertex index in buffer
328+ buffer[m+ 1 ] = length (vertices)
329+ push! (vertices, Point {3,T} (v[1 ],v[2 ],v[3 ]))
330+
331+ # Now we need to add faces together, to do this we just loop over 3 basis components
332+ for i= 0 : 2
333+ # The first three entries of the edge_mask count the crossings along the edge
334+ if (edge_mask & (1 << i)) == 0
335+ continue
336+ end
337+
338+ # i = axes we are point along. iu, iv = orthogonal axes
339+ iu = (i+ 1 )% 3
340+ iv = (i+ 2 )% 3
341+
342+ # If we are on a boundary, skip it
343+ if (x[iu+ 1 ] == 0 || x[iv+ 1 ] == 0 )
344+ continue
345+ end
346+
347+ # Otherwise, look up adjacent edges in buffer
348+ du = R[iu+ 1 ]
349+ dv = R[iv+ 1 ]
350+
351+ # Remember to flip orientation depending on the sign of the corner.
352+ if (mask & 0x01 ) != 0x00
353+ push! (faces,Face {4,Int} (buffer[m+ 1 ]+ 1 , buffer[m- du+ 1 ]+ 1 , buffer[m- du- dv+ 1 ]+ 1 , buffer[m- dv+ 1 ]+ 1 ));
354+ else
355+ push! (faces,Face {4,Int} (buffer[m+ 1 ]+ 1 , buffer[m- dv+ 1 ]+ 1 , buffer[m- du- dv+ 1 ]+ 1 , buffer[m- du+ 1 ]+ 1 ));
356+ end
357+ end
358+ x[1 ] += 1
359+ n += 1
360+ m += 1
361+ end
362+ x[2 ] += 1
363+ n += 1
364+ m += 2
365+ end
366+ x[3 ] += 1
367+ n+= dims[1 ]
368+ buf_no = xor (buf_no,1 )
369+ R[3 ]= - R[3 ]
370+ end
371+ # All done! Return the result
372+
373+ vertices, faces # faces are quads, indexed to vertices
374+ end
375+
376+
208377struct NaiveSurfaceNets{T} <: AbstractMeshingAlgorithm
209378 iso:: T
210379 eps:: T
@@ -235,3 +404,18 @@ function (::Type{MT})(sdf::SignedDistanceField, method::NaiveSurfaceNets) where
235404 orig)
236405 MT (vts, fcs):: MT
237406end
407+
408+ function (:: Type{MT} )(f:: Function , bounds:: HyperRectangle , size:: NTuple{3,Int} , method:: NaiveSurfaceNets ) where {MT <: AbstractMesh }
409+ orig = origin (bounds)
410+ w = widths (bounds)
411+ scale = w ./ Point (size .- 1 ) # subtract 1 because an SDF with N points per side has N-1 cells
412+
413+ # TODO ISO val
414+
415+ vts, fcs = surface_nets (f,
416+ size,
417+ method. eps,
418+ scale,
419+ orig)
420+ MT (vts, fcs):: MT
421+ end
0 commit comments