1- function getsemihull (ps:: Vector{PT} , sign_sense, counterclockwise, yray:: Nothing = nothing ) where PT
1+ function _semi_hull (ps:: Vector{PT} , sign_sense, counterclockwise, sweep_norm , yray:: Nothing = nothing ) where PT
22 hull = PT[]
3- if length (ps) == 0
3+ if isempty (ps)
44 return hull
55 end
66 prev = sign_sense == 1 ? first (ps) : last (ps)
@@ -17,12 +17,22 @@ function getsemihull(ps::Vector{PT}, sign_sense, counterclockwise, yray::Nothing
1717 psj_vec = ps[j] - prev
1818 cc = counterclockwise (cur_vec, psj_vec)
1919 if isapproxzero (cc)
20- # `cur` and `ps[j]` are on the same ray starting from `prev`.
21- # The one that is closer to `prev` is redundant.
22- # If `j` is the last index and redundant (it may happen if this
23- # ray is perpendicular to the direction of sorting) then we should
24- # also avoid adding `ps[j]` to `hull` so we set `skip` to `true`.
25- if norm (cur_vec, 1 ) > norm (psj_vec, 1 )
20+ # `prev`, `cur` and `ps[j]` are on the same line
21+ # Two cases here:
22+ # 1) `prev`, `cur`, `ps[j]` are on a line perpendicular to `sweep_norm`
23+ # The one that is not clockwise is redundant.
24+ # 2) `cur`, `ps[j]` and on the same ray starting from `prev` of direction
25+ # `sweep_norm * sign_sense`
26+ # The one that is closer to `prev` is redundant.
27+ dcur = dot (cur_vec, sweep_norm)
28+ dpsj = dot (psj_vec, sweep_norm)
29+ if isapproxzero (dcur) && isapproxzero (dpsj)
30+ # Case 1
31+ if sign_sense * counterclockwise (cur_vec, sweep_norm) < sign_sense * counterclockwise (psj_vec, sweep_norm)
32+ skip = true
33+ break
34+ end
35+ elseif sign_sense * dcur > sign_sense * dpsj
2636 skip = true
2737 break
2838 end
@@ -46,14 +56,16 @@ function getsemihull(ps::Vector{PT}, sign_sense, counterclockwise, yray::Nothing
4656 return hull
4757end
4858
49- function getsemihull (ps:: Vector{PT} , sign_sense, counterclockwise, yray) where PT
50- hull = getsemihull (ps, sign_sense, counterclockwise)
59+ function _semi_hull (ps:: Vector{PT} , sign_sense, counterclockwise, sweep_norm , yray) where PT
60+ hull = _semi_hull (ps, sign_sense, counterclockwise, sweep_norm )
5161 while length (hull) >= 2 && counterclockwise (hull[end ] - hull[end - 1 ], yray) >= 0
5262 pop! (hull)
5363 end
5464 return hull
5565end
5666
67+ _colinear (counterclockwise, a, b, c) = isapproxzero (counterclockwise (b - a, c - a))
68+
5769function _planar_hull (d:: FullDim , points, lines, rays, counterclockwise, rotate)
5870 line = nothing
5971 lineleft = false
@@ -124,32 +136,31 @@ function _planar_hull(d::FullDim, points, lines, rays, counterclockwise, rotate)
124136 else
125137 sweep_norm = rotate (coord (line))
126138 end
127- sort! (points, by = x -> dot (x, sweep_norm))
128-
129- if ! isempty (points)
130- # `getsemihull` fails if `points` starts or end with several points on the same sweep line that are not ordered clockwise
131- start_line = dot (points[1 ], sweep_norm)
132- # `isapprox` won't work well with `atol=0` (which is the default) if `start_line` is zero, so we set a nonzero `atol`.
133- # TODO We should also multiply it with a scaling.
134- end_start = something (findfirst (x -> ! isapprox (dot (x, sweep_norm), start_line, atol= Base. rtoldefault (typeof (start_line))), points), length (points) + 1 ) - 1
135- if end_start > 1
136- sort! (view (points, 1 : end_start), by = x -> counterclockwise (x, sweep_norm), rev= true )
137- end
138- end_line = dot (points[end ], sweep_norm)
139- start_end = something (findlast (x -> ! isapprox (dot (x, sweep_norm), end_line, atol= Base. rtoldefault (typeof (start_line))), points), 0 ) + 1
140- if start_end < length (points)
141- sort! (view (points, start_end: length (points)), by = x -> counterclockwise (x, sweep_norm))
142- end
143- end
139+ sort! (points, by = Base. Fix2 (dot, sweep_norm))
144140
145141 _points = eltype (points)[]
146142 _lines = eltype (lines)[]
147143 _rays = eltype (rays)[]
148144 if line === nothing
149- append! (_points, getsemihull (points, 1 , counterclockwise, yray) )
145+ half_points = _semi_hull (points, 1 , counterclockwise, sweep_norm, yray)
150146 if yray === nothing
151- append! (_points, getsemihull (points, - 1 , counterclockwise, yray)[2 : end - 1 ])
147+ other_half = _semi_hull (points, - 1 , counterclockwise, sweep_norm, yray)[2 : end - 1 ]
148+ if ! isempty (other_half) && length (half_points) > 1 &&
149+ _colinear (counterclockwise, half_points[1 ], half_points[2 ], other_half[end ])
150+ start = 2
151+ else
152+ start = 1
153+ end
154+ if ! isempty (other_half) && length (half_points) > 1 &&
155+ _colinear (counterclockwise, half_points[end ], half_points[end - 1 ], other_half[1 ])
156+ stop = length (half_points) - 1
157+ else
158+ stop = length (half_points)
159+ end
160+ append! (_points, half_points[start: stop])
161+ append! (_points, other_half)
152162 else
163+ append! (_points, half_points)
153164 push! (_rays, Ray (xray))
154165 if ! (Ray (xray) ≈ Ray (yray))
155166 push! (_rays, Ray (yray))
0 commit comments