@@ -5,13 +5,15 @@ function _semi_hull(ps::Vector{PT}, sign_sense, counterclockwise, sweep_norm, yr
55 end
66 prev = sign_sense == 1 ? first (ps) : last (ps)
77 cur = prev
8+ flat_start = true
89 # Invariant:
910 # We either have:
1011 # * `hull` is empty and `cur == prev` or
1112 # * `hull` is nonempty and `prev = last(hull)`.
1213 # In any case, the semihull of `ps[1:(j-1)]` is given by `[hull; cur]`.
1314 for j in (sign_sense == 1 ? (2 : length (ps)) : ((length (ps)- 1 ): - 1 : 1 ))
1415 skip = false
16+ flat = false
1517 while prev != cur
1618 cur_vec = cur - prev
1719 psj_vec = ps[j] - prev
@@ -26,9 +28,30 @@ function _semi_hull(ps::Vector{PT}, sign_sense, counterclockwise, sweep_norm, yr
2628 # The one that is closer to `prev` is redundant.
2729 dcur = dot (cur_vec, sweep_norm)
2830 dpsj = dot (psj_vec, sweep_norm)
29- if isapproxzero (dcur) && isapproxzero (dpsj)
31+ flat = isapproxzero (dcur) && isapproxzero (dpsj)
32+ if flat
3033 # Case 1
31- if sign_sense * counterclockwise (cur_vec, sweep_norm) < sign_sense * counterclockwise (psj_vec, sweep_norm)
34+ # There can be two flat plateaus. The first one at the start and then one at the end
35+ # `flat_start` indicates in which case we are. We need to a different direction in both cases
36+ sense = flat_start ? sign_sense : - sign_sense
37+ ccjsweep = sense * counterclockwise (psj_vec, sweep_norm)
38+ cccursweep = sense * counterclockwise (cur_vec, sweep_norm)
39+ if cccursweep < 0 < ccjsweep
40+ # In this case, `prev` -> `cur` was in the right direction but now that
41+ # we discover `ps[j]`, we realize that `prev` is in the interval
42+ # `[ps[j], cur]`. Even if `ps[j]` -> `cur` seems to be in the right direction, we cannot
43+ # keep it because it is in the wrong order in `ps`, it will be picked up by the other
44+ # call to `_semi_hull` in which case `ps[j]` should be skipped (this is typically the case for the first flat plateau)
45+ # For the second (and last flat plateau), it will rather be the case that `cur` should be removed.
46+ # The only thing that is sure here is that `prev` should be removed so we remove `prev` and we `continue`
47+ # as the code should then automatically decide which of `ps[j]` or `cur` should be removed
48+ pop! (hull)
49+ prev = cur
50+ if ! isempty (hull)
51+ prev = last (hull)
52+ end
53+ continue
54+ elseif cccursweep < ccjsweep
3255 skip = true
3356 break
3457 end
@@ -46,6 +69,9 @@ function _semi_hull(ps::Vector{PT}, sign_sense, counterclockwise, sweep_norm, yr
4669 prev = last (hull)
4770 end
4871 end
72+ if prev != cur && ! flat
73+ flat_start = false
74+ end
4975 if ! skip
5076 push! (hull, cur)
5177 prev = cur
0 commit comments