Skip to content

Commit a710315

Browse files
authored
Merge pull request #50 from JuliaGeometry/sjk/lut2
Implement performance improvements using bit masking
2 parents 6e963f5 + 8c8c5c5 commit a710315

File tree

1 file changed

+45
-33
lines changed

1 file changed

+45
-33
lines changed

src/Contour.jl

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ const NS, NE, NW = N|S, N|E, N|W
141141
const SN, SE, SW = S|N, S|E, S|W
142142
const EN, ES, EW = E|N, E|S, E|W
143143
const WN, WS, WE = W|N, W|S, W|E
144+
const NWSE = NW | 0x10 # special (ambiguous case)
145+
const NESW = NE | 0x10 # special (ambiguous case)
144146

145147
const dirStr = ["N", "S", "NS", "E", "NE", "NS", "Invalid crossing",
146148
"W", "NW", "SW", "Invalid crossing", "WE"]
@@ -151,20 +153,42 @@ const dirStr = ["N", "S", "NS", "E", "NE", "NS", "Invalid crossing",
151153
# the type of crossing that a cell contains. While most
152154
# cells will have only one crossing, cell type 5 and 10 will
153155
# have two crossings.
154-
struct Cell
155-
crossings::Vector{UInt8}
156+
function get_next_edge!(cells::Dict, xi, yi, entry_edge::UInt8)
157+
key = (xi,yi)
158+
cell = cells[key]
159+
if iszero(cell & 0x10)
160+
next_edge = cell entry_edge
161+
delete!(cells, key)
162+
return next_edge
163+
else # ambiguous case flag
164+
if cell == NWSE
165+
if !iszero(NW & entry_edge)
166+
cells[key] = SE
167+
return NW entry_edge
168+
elseif !iszero(SE & entry_edge)
169+
cells[key] = NW
170+
return SE entry_edge
171+
end
172+
elseif cell == NESW
173+
if !iszero(NE & entry_edge)
174+
cells[key] = SW
175+
return NE entry_edge
176+
elseif !iszero(SW & entry_edge)
177+
cells[key] = NE
178+
return SW entry_edge
179+
end
180+
end
181+
end
156182
end
157183

158-
function get_next_edge!(cell::Cell, entry_edge::UInt8)
159-
for (i, edge) in enumerate(cell.crossings)
160-
if !iszero(edge & entry_edge)
161-
next_edge = edge entry_edge
162-
deleteat!(cell.crossings, i)
163-
164-
return next_edge
165-
end
184+
function get_first_crossing(cell)
185+
if iszero(cell & 0x10)
186+
return cell
187+
elseif cell == NWSE
188+
return NW
189+
elseif cell == NESW
190+
return NE
166191
end
167-
error("There is no edge containing ", entry_edge)
168192
end
169193

170194
# Maps cell type to crossing types for non-ambiguous cells
@@ -179,7 +203,7 @@ function _get_case(z, h)
179203
end
180204

181205
function get_level_cells(z, h::Number)
182-
cells = Dict{(Tuple{Int,Int}),Cell}()
206+
cells = Dict{Tuple{Int,Int},UInt8}()
183207
xi_max, yi_max = size(z)
184208

185209
@inbounds for xi in 1:xi_max - 1
@@ -196,18 +220,18 @@ function get_level_cells(z, h::Number)
196220
# a bilinear interpolation of the cell-center value.
197221
if case == 0x05
198222
if 0.25*sum(elts) >= h
199-
cells[(xi, yi)] = Cell([NW, SE])
223+
cells[(xi, yi)] = NWSE
200224
else
201-
cells[(xi, yi)] = Cell([NE, SW])
225+
cells[(xi, yi)] = NESW
202226
end
203227
elseif case == 0x0a
204228
if 0.25*sum(elts) >= h
205-
cells[(xi, yi)] = Cell([NE, SW])
229+
cells[(xi, yi)] = NESW
206230
else
207-
cells[(xi, yi)] = Cell([NW, SE])
231+
cells[(xi, yi)] = NWSE
208232
end
209233
else
210-
cells[(xi, yi)] = Cell([edge_LUT[case]])
234+
cells[(xi, yi)] = edge_LUT[case]
211235
end
212236
end
213237
end
@@ -219,7 +243,7 @@ end
219243

220244
const fwd, rev = (UInt8(0)), (UInt8(1))
221245

222-
@inline function add_vertex!(curve::Curve2{T}, pos::Tuple{T,T}, dir::UInt8) where {T}
246+
function add_vertex!(curve::Curve2{T}, pos::Tuple{T,T}, dir::UInt8) where {T}
223247
if dir == fwd
224248
push!(curve.vertices, SVector{2,T}(pos...))
225249
else
@@ -284,11 +308,7 @@ function chase!(cells, curve, x, y, z, h, xi_start, yi_start, entry_edge, xi_max
284308
loopback_edge = entry_edge
285309

286310
while true
287-
cell = cells[(xi, yi)]
288-
exit_edge = get_next_edge!(cell, entry_edge)
289-
if length(cell.crossings) == 0
290-
delete!(cells, (xi, yi))
291-
end
311+
exit_edge = get_next_edge!(cells, xi, yi, entry_edge)
292312

293313
add_vertex!(curve, interpolate(x, y, z, h, xi, yi, exit_edge), dir)
294314

@@ -313,18 +333,10 @@ function chase!(cells, curve, x, y, z, h, xi_start, yi_start, entry_edge, xi_max
313333
end
314334

315335

316-
function trace_contour(x, y, z, h::Number, cells::Dict{(Tuple{Int,Int}),Cell})
336+
function trace_contour(x, y, z, h::Number, cells::Dict)
317337

318338
contours = ContourLevel(h)
319339

320-
local yi::Int
321-
local xi::Int
322-
local xi_0::Int
323-
local yi_0::Int
324-
325-
local xi_max::Int
326-
local yi_max::Int
327-
328340
(xi_max, yi_max) = size(z)
329341

330342
# When tracing out contours, this algorithm picks an arbitrary
@@ -340,7 +352,7 @@ function trace_contour(x, y, z, h::Number, cells::Dict{(Tuple{Int,Int}),Cell})
340352
(xi, yi) = (xi_0, yi_0)
341353

342354
# Pick a starting edge
343-
crossing = first(cell.crossings)
355+
crossing = get_first_crossing(cell)
344356
starting_edge = UInt8(0)
345357
for edge in (N, S, E, W)
346358
if !iszero(edge & crossing)

0 commit comments

Comments
 (0)