@@ -2,6 +2,8 @@ module Contour
2
2
3
3
using StaticArrays
4
4
5
+ include (" interpolate.jl" )
6
+
5
7
export
6
8
ContourLevel,
7
9
Curve2,
@@ -144,42 +146,48 @@ const WN, WS, WE = W|N, W|S, W|E
144
146
const NWSE = NW | 0x10 # special (ambiguous case)
145
147
const NESW = NE | 0x10 # special (ambiguous case)
146
148
147
- const dirStr = [ " N " , " S " , " NS " , " E " , " NE " , " NS " , " Invalid crossing " ,
148
- " W " , " NW " , " SW " , " Invalid crossing " , " WE " ]
149
+ # Maps cell type to crossing types for non-ambiguous cells
150
+ const edge_LUT = (SW, SE, EW, NE, 0x0 , NS, NW, NW, NS, 0x0 , NE, EW, SE, SW)
149
151
150
152
# The way a contour crossing goes through a cell is labeled
151
153
# by combining compass directions (e.g. a NW crossing connects
152
154
# the N edge and W edges of the cell). The Cell type records
153
155
# the type of crossing that a cell contains. While most
154
156
# cells will have only one crossing, cell type 5 and 10 will
155
157
# have two crossings.
156
- function get_next_edge! (cells:: Dict , xi, yi, entry_edge:: UInt8 )
157
- key = (xi,yi)
158
- cell = cells[key]
158
+ function get_next_edge! (cells:: Dict , key, entry_edge:: UInt8 )
159
+ cell = pop! (cells, key)
159
160
if cell == NWSE
160
- if ! iszero (NW & entry_edge)
161
+ if entry_edge == N || entry_edge == W
161
162
cells[key] = SE
162
- return NW ⊻ entry_edge
163
- else # Nw
163
+ cell = NW
164
+ else # SE
164
165
cells[key] = NW
165
- return SE ⊻ entry_edge
166
+ cell = SE
166
167
end
167
168
elseif cell == NESW
168
- if ! iszero (NE & entry_edge)
169
+ if entry_edge == N || entry_edge == E
169
170
cells[key] = SW
170
- return NE ⊻ entry_edge
171
+ cell = NE
171
172
else # SW
172
173
cells[key] = NE
173
- return SW ⊻ entry_edge
174
+ cell = SW
174
175
end
175
- else
176
- next_edge = cell ⊻ entry_edge
177
- delete! (cells, key)
178
- return next_edge
179
176
end
177
+ return cell ⊻ entry_edge
180
178
end
181
179
182
- function get_first_crossing (cell)
180
+ # N, S, E, W
181
+ const next_map = ((0 ,1 ), (0 ,- 1 ), (1 ,0 ), (- 1 ,0 ))
182
+ const next_edge = (S,N,W,E)
183
+
184
+ @inline function advance_edge (ind, edge)
185
+ n = trailing_zeros (edge) + 1
186
+ nt = ind .+ next_map[n]
187
+ return nt, next_edge[n]
188
+ end
189
+
190
+ @inline function get_first_crossing (cell)
183
191
if cell == NWSE
184
192
return NW
185
193
elseif cell == NESW
@@ -189,10 +197,7 @@ function get_first_crossing(cell)
189
197
end
190
198
end
191
199
192
- # Maps cell type to crossing types for non-ambiguous cells
193
- const edge_LUT = (SW, SE, EW, NE, 0x0 , NS, NW, NW, NS, 0x0 , NE, EW, SE, SW)
194
-
195
- function _get_case (z, h)
200
+ @inline function _get_case (z, h)
196
201
case = z[1 ] > h ? 0x01 : 0x00
197
202
z[2 ] > h && (case |= 0x02 )
198
203
z[3 ] > h && (case |= 0x04 )
@@ -229,67 +234,12 @@ function get_level_cells(z, h::Number)
229
234
return cells
230
235
end
231
236
232
- # Some constants used by trace_contour
233
-
234
- const fwd, rev = (false , true )
235
-
236
- function add_vertex! (curve:: Curve2{T} , pos:: Tuple{T,T} , dir) where {T}
237
- if dir == fwd
238
- push! (curve. vertices, SVector {2,T} (pos... ))
239
- else
240
- pushfirst! (curve. vertices, SVector {2,T} (pos... ))
241
- end
242
- end
243
-
244
- # Given the row and column indices of the lower left
245
- # vertex, add the location where the contour level
246
- # crosses the specified edge.
247
- function interpolate (x, y, z:: AbstractMatrix{T} , h:: Number , xi:: Int , yi:: Int , edge:: UInt8 ) where {T <: AbstractFloat }
248
- @inbounds if edge == W
249
- y_interp = y[yi] + (y[yi + 1 ] - y[yi]) * (h - z[xi, yi]) / (z[xi, yi + 1 ] - z[xi, yi])
250
- x_interp = x[xi]
251
- elseif edge == E
252
- y_interp = y[yi] + (y[yi + 1 ] - y[yi]) * (h - z[xi + 1 , yi]) / (z[xi + 1 , yi + 1 ] - z[xi + 1 , yi])
253
- x_interp = x[xi + 1 ]
254
- elseif edge == N
255
- y_interp = y[yi + 1 ]
256
- x_interp = x[xi] + (x[xi + 1 ] - x[xi]) * (h - z[xi, yi + 1 ]) / (z[xi + 1 , yi + 1 ] - z[xi, yi + 1 ])
257
- elseif edge == S
258
- y_interp = y[yi]
259
- x_interp = x[xi] + (x[xi + 1 ] - x[xi]) * (h - z[xi, yi]) / (z[xi + 1 , yi] - z[xi, yi])
260
- end
261
-
262
- return x_interp, y_interp
263
- end
264
-
265
- function interpolate (x:: AbstractMatrix{T} , y:: AbstractMatrix{T} , z:: AbstractMatrix{T} , h:: Number , xi:: Int , yi:: Int , edge:: UInt8 ) where {T <: AbstractFloat }
266
- if edge == W
267
- Δ = [y[xi, yi+ 1 ] - y[xi, yi ], x[xi, yi+ 1 ] - x[xi, yi ]]. * (h - z[xi, yi ])/ (z[xi, yi+ 1 ] - z[xi, yi ])
268
- y_interp = y[xi,yi] + Δ[1 ]
269
- x_interp = x[xi,yi] + Δ[2 ]
270
- elseif edge == E
271
- Δ = [y[xi+ 1 ,yi+ 1 ] - y[xi+ 1 ,yi ], x[xi+ 1 ,yi+ 1 ] - x[xi+ 1 ,yi ]]. * (h - z[xi+ 1 ,yi ])/ (z[xi+ 1 ,yi+ 1 ] - z[xi+ 1 ,yi ])
272
- y_interp = y[xi+ 1 ,yi] + Δ[1 ]
273
- x_interp = x[xi+ 1 ,yi] + Δ[2 ]
274
- elseif edge == N
275
- Δ = [y[xi+ 1 ,yi+ 1 ] - y[xi, yi+ 1 ], x[xi+ 1 ,yi+ 1 ] - x[xi, yi+ 1 ]]. * (h - z[xi, yi+ 1 ])/ (z[xi+ 1 ,yi+ 1 ] - z[xi, yi+ 1 ])
276
- y_interp = y[xi,yi+ 1 ] + Δ[1 ]
277
- x_interp = x[xi,yi+ 1 ] + Δ[2 ]
278
- elseif edge == S
279
- Δ = [y[xi+ 1 ,yi ] - y[xi, yi ], x[xi+ 1 ,yi ] - x[xi, yi ]]. * (h - z[xi, yi ])/ (z[xi+ 1 ,yi ] - z[xi, yi ])
280
- y_interp = y[xi,yi] + Δ[1 ]
281
- x_interp = x[xi,yi] + Δ[2 ]
282
- end
283
-
284
- return x_interp, y_interp
285
- end
286
-
287
237
288
238
# Given a cell and a starting edge, we follow the contour line until we either
289
239
# hit the boundary of the input data, or we form a closed contour.
290
- function chase! (cells, curve, x, y, z, h, xi_start, yi_start, entry_edge, xi_max, yi_max, dir)
240
+ function chase! (cells, curve, x, y, z, h, start, entry_edge, xi_max, yi_max, :: Type{VT} ) where VT
291
241
292
- xi, yi = xi_start, yi_start
242
+ ind = start
293
243
294
244
# When the contour loops back to the starting cell, it is possible
295
245
# for it to not intersect with itself. This happens if the starting
@@ -298,92 +248,62 @@ function chase!(cells, curve, x, y, z, h, xi_start, yi_start, entry_edge, xi_max
298
248
loopback_edge = entry_edge
299
249
300
250
@inbounds while true
301
- exit_edge = get_next_edge! (cells, xi, yi, entry_edge)
302
-
303
- add_vertex! (curve, interpolate (x, y, z, h, xi, yi, exit_edge), dir)
304
-
305
- if exit_edge == N
306
- yi += 1
307
- entry_edge = S
308
- elseif exit_edge == S
309
- yi -= 1
310
- entry_edge = N
311
- elseif exit_edge == E
312
- xi += 1
313
- entry_edge = W
314
- elseif exit_edge == W
315
- xi -= 1
316
- entry_edge = E
317
- end
318
- ! ((xi, yi, entry_edge) != (xi_start, yi_start, loopback_edge) &&
319
- 0 < yi < yi_max && 0 < xi < xi_max) && break
251
+ exit_edge = get_next_edge! (cells, ind, entry_edge)
252
+
253
+ push! (curve, interpolate (x, y, z, h, ind, exit_edge, VT))
254
+
255
+ ind, entry_edge = advance_edge (ind, exit_edge)
256
+
257
+ ! ((ind[1 ], ind[2 ], entry_edge) != (start[1 ], start[2 ], loopback_edge) &&
258
+ 0 < ind[2 ] < yi_max && 0 < ind[1 ] < xi_max) && break
320
259
end
321
260
322
- return xi, yi
261
+ return ind
323
262
end
324
263
325
264
326
265
function trace_contour (x, y, z, h:: Number , cells:: Dict )
327
266
328
267
contours = ContourLevel (h)
329
268
330
- (xi_max, yi_max) = size (z)
269
+ (xi_max, yi_max) = size (z):: Tuple{Int,Int}
270
+
271
+ VT = SVector{2 ,promote_type (map (eltype, (x, y, z))... )}
331
272
332
273
# When tracing out contours, this algorithm picks an arbitrary
333
274
# starting cell, then first follows the contour in one direction
334
275
# until it either ends up where it started # or at one of the boundaries.
335
276
# It then tries to trace the contour in the opposite direction.
336
277
337
278
@inbounds while length (cells) > 0
338
- contour = Curve2 ( promote_type ( map (eltype, (x, y, z)) ... ))
279
+ contour_arr = VT[]
339
280
340
281
# Pick initial box
341
- (xi_0, yi_0), cell = first (cells)
342
- (xi, yi) = (xi_0, yi_0)
282
+ ind, cell = first (cells)
343
283
344
284
# Pick a starting edge
345
285
crossing = get_first_crossing (cell)
346
- starting_edge = UInt8 (0 )
347
- for edge in (N, S, E, W)
348
- if ! iszero (edge & crossing)
349
- starting_edge = edge
350
- break
351
- end
352
- end
286
+ starting_edge = 0x01 << trailing_zeros (crossing)
353
287
354
288
# Add the contour entry location for cell (xi_0,yi_0)
355
- add_vertex! (contour , interpolate (x, y, z, h, xi_0, yi_0, starting_edge), fwd )
289
+ push! (contour_arr , interpolate (x, y, z, h, ind, starting_edge, VT) )
356
290
357
291
# Start trace in forward direction
358
- (xi_end, yi_end) = chase! (cells, contour , x, y, z, h, xi, yi, starting_edge, xi_max, yi_max, fwd )
292
+ ind_end = chase! (cells, contour_arr , x, y, z, h, ind, starting_edge, xi_max, yi_max, VT )
359
293
360
- if (xi_end, yi_end) == (xi_0, yi_0)
361
- push! (contours. lines, contour )
294
+ if ind == ind_end
295
+ push! (contours. lines, Curve2 (contour_arr) )
362
296
continue
363
297
end
364
298
365
- if starting_edge == N
366
- yi = yi_0 + 1
367
- starting_edge = S
368
- elseif starting_edge == S
369
- yi = yi_0 - 1
370
- starting_edge = N
371
- elseif starting_edge == E
372
- xi = xi_0 + 1
373
- starting_edge = W
374
- elseif starting_edge == W
375
- xi = xi_0 - 1
376
- starting_edge = E
377
- end
299
+ ind, starting_edge = advance_edge (ind, starting_edge)
378
300
379
- if ! ( 0 < yi < yi_max && 0 < xi < xi_max)
380
- push! (contours . lines, contour)
381
- continue
301
+ if 0 < ind[ 2 ] < yi_max && 0 < ind[ 1 ] < xi_max
302
+ # Start trace in reverse direction
303
+ chase! (cells, reverse! (contour_arr), x, y, z, h, ind, starting_edge, xi_max, yi_max, VT)
382
304
end
383
305
384
- # Start trace in reverse direction
385
- (xi, yi) = chase! (cells, contour, x, y, z, h, xi, yi, starting_edge, xi_max, yi_max, rev)
386
- push! (contours. lines, contour)
306
+ push! (contours. lines, Curve2 (contour_arr))
387
307
end
388
308
389
309
return contours
0 commit comments