From 6911b8bbf8792721a926d136f4ffbbf9d601b72c Mon Sep 17 00:00:00 2001 From: Sebastian Guadalupe Date: Wed, 12 Jun 2019 23:18:25 -0300 Subject: [PATCH 01/13] add four vertex case for convex hull in 2d --- src/concrete_convex_hull.jl | 129 +++++++++++++++++++++++++++++++++++- test/unit_convex_hull.jl | 55 +++++++++++++-- 2 files changed, 178 insertions(+), 6 deletions(-) diff --git a/src/concrete_convex_hull.jl b/src/concrete_convex_hull.jl index 3ac54df7d0..9022a1d371 100644 --- a/src/concrete_convex_hull.jl +++ b/src/concrete_convex_hull.jl @@ -28,8 +28,10 @@ The convex hull as a list of vectors with the coordinates of the points. ### Algorithm -A pre-processing step treats the cases with `0`, `1`, `2` and `3` points in any dimension. -For `4` or more points, the algorithm used depends on the dimension. +A pre-processing step treats the cases with `0`, `1` and `2` points for one +dimension, `0`, `1`, `2`, `3` and `4` for two dimensions, if there is more +points, its used a general algorithm for one and two dimensions. +For higher dimension there is a general algorithm. For the one-dimensional case we return the minimum and maximum points, in that order. @@ -114,6 +116,9 @@ function convex_hull!(points::Vector{VN}; elseif m == 3 # three points case in 2d return _three_points_2d!(points) + elseif m == 4 + # four points case in 2d + return _four_points_2d!(points) else # general case in 2d return _convex_hull_2d!(points, algorithm=algorithm) @@ -186,6 +191,126 @@ function _three_points_2d!(points::AbstractVector{<:AbstractVector{N}}) where {N return points end +function _four_points_2d!(points::AbstractVector{<:AbstractVector{N}}) where {N<:Real} + A, B, C, D = points[1], points[2], points[3], points[4] + tri_ABC = (A[2] - B[2]) * C[1] + (B[1] - A[1]) * C[2] + (A[1] * B[2] - B[1] * A[2]) + tri_ABD = (A[2] - B[2]) * D[1] + (B[1] - A[1]) * D[2] + (A[1] * B[2] - B[1] * A[2]) + tri_BCD = (B[2] - C[2]) * D[1] + (C[1] - B[1]) * D[2] + (B[1] * C[2] - C[1] * B[2]) + tri_CAD = (C[2] - A[2]) * D[1] + (A[1] - C[1]) * D[2] + (C[1] * A[2] - A[1] * C[2]) + key = 0 + if tri_ABC > zero(N) + key = key + 1000 + end + if tri_ABD > zero(N) + key = key + 100 + end + if tri_BCD > zero(N) + key = key + 10 + end + if tri_CAD > zero(N) + key = key + 1 + end + + function collinear_case(A, B, C, D) + # A, B and C collinear, D is the extra point + if isapprox(A[1], B[1]) && isapprox(B[1], C[1]) && isapprox(C[1], A[1]) + # points are approximately equal in their first component + if isapprox(A[2], B[2]) && isapprox(B[2], C[2]) && isapprox(C[2], A[2]) + # the three points are approximately equal + points[1], points[2] = A, D + pop!(points) + pop!(points) + return _two_points_2d!(points) + else + # assign the points with max and min value in their second component to the + # firsts points and the extra point to the third place, then pop the point that was in the middle + points[1], points[2], points[3] = points[argmin([A[2], B[2], C[2]])], points[argmax([A[2], B[2], C[2]])], D + pop!(points) + end + else + # assign the points with max and min value in their first component to the + # firsts points and the extra point to the third place, then pop the point that was in the middle + points[1], points[2], points[3] = points[argmin([A[1], B[1], C[1]])], points[argmax([A[1], B[1], C[1]])], D + pop!(points) + end + return _three_points_2d!(points) + end + + if isapproxzero(tri_ABC) + return collinear_case(A, B, C, D) + end + if isapproxzero(tri_ABD) + return collinear_case(A, B, D, C) + end + if isapproxzero(tri_BCD) + return collinear_case(B, C, D, A) + end + if isapproxzero(tri_CAD) + return collinear_case(C, A, D, B) + end + + # ABC ABD BCD CAD hull + # ------------------------ + # + + + + ABC + # + + + - ABCD + # + + - + ABDC + # + + - - ABD + # + - + + ADBC + # + - + - BCD + # + - - + CAD + # + - - - [should not happen] + # - + + + [should not happen] + # - + + - ACD + # - + - + DCB + # - + - - DBCA + # - - + + ADB + # - - + - ACDB + # - - - + ADCB + # - - - - ACB + if key == 1111 + points[1], points[2], points[3] = A, B, C # + + + + ABC + pop!(points) + elseif key == 1110 + points[1], points[2], points[3], points[4] = A, B, C, D # + + + - ABCD + elseif key == 1101 + points[1], points[2], points[3], points[4] = A, B, D, C # + + - + ABDC + elseif key == 1100 + points[1], points[2], points[3] = A, B, D # + + - - ABD + pop!(points) + elseif key == 1011 + points[1], points[2], points[3], points[4] = A, D, B, C # + - + + ADBC + elseif key == 1010 + points[1], points[2], points[3] = B, C, D # + - + - BCD + pop!(points) + elseif key == 1001 + points[1], points[2], points[3] = C, A, D # + - - + CAD + pop!(points) + elseif key == 1000 + # + - - - [should not happen] + elseif key == 0111 + # - + + + [should not happen] + elseif key == 0110 + points[1], points[2], points[3] = A, C, D # - + + - ACD + pop!(points) + elseif key == 0101 + points[1], points[2], points[3] = D, C, B # - + - + DCB + pop!(points) + elseif key == 0100 + points[1], points[2], points[3], points[4] = D, B, C, A # - + - - DBCA + elseif key == 0011 + points[1], points[2], points[3] = A, D, B # - - + + ADB + pop!(points) + elseif key == 0010 + points[1], points[2], points[3], points[4] = A, C, D, B # - - + - ACDB + elseif key == 0001 + points[1], points[2], points[3], points[4] = A, D, C, B # - - - + ADCB + elseif key == 0000 + points[1], points[2], points[3] = A, C, B # - - - - ACB + pop!(points) + end + return points +end + function _convex_hull_1d!(points::Vector{VN})::Vector{VN} where {N<:Real, VN<:AbstractVector{N}} points[1:2] = [minimum(points), maximum(points)] return resize!(points, 2) diff --git a/test/unit_convex_hull.jl b/test/unit_convex_hull.jl index 3916e30d62..f0fe37a051 100644 --- a/test/unit_convex_hull.jl +++ b/test/unit_convex_hull.jl @@ -33,23 +33,70 @@ for N in [Float64, Rational{Int}] convex_hull!(points_2D) # check in-place version @test points_2D == [[N(-1), N(-1)], [N(1), N(0)], [N(1), N(1)], [N(0), N(1)]] - # three vertex case in 2 dimensions - function iscounterclockwise(result, correct_expr) + function iscounterclockwise_four_points(result, correct_expr) + # this function checks if the result equals any of the correct answer cyclic permutation + result == correct_expr || result == circshift(correct_expr, 1) || result == circshift(correct_expr, 2) || result == circshift(correct_expr, 3) + end + + function iscounterclockwise_three_points(result, correct_expr) # this function checks if the result equals any of the correct answer cyclic permutation result == correct_expr || result == circshift(correct_expr, 1) || result == circshift(correct_expr, 2) end + + # three vertex case in 2 dimensions ccw_points = [N[1, 1], N[-1, 1], N[-1, 0]] ccw_p = convex_hull!(ccw_points) ccw_expr = [N[1, 1], N[-1, 1], N[-1, 0]] - @test iscounterclockwise(ccw_p, ccw_expr) # points sorted in a counter-clockwise fashion + @test iscounterclockwise_three_points(ccw_p, ccw_expr) # points sorted in a counter-clockwise fashion cw_points = [N[-1, 1], N[1, 1], N[-1, 0]] cw_p = convex_hull!(cw_points) cw_expr = [N[1, 1], N[-1, 1], N[-1, 0]] - @test iscounterclockwise(cw_p, cw_expr) # points sorted in clockwise fashion + @test iscounterclockwise_three_points(cw_p, cw_expr) # points sorted in clockwise fashion @test ispermutation(convex_hull!([N[1, 1], N[2, 2], N[3, 3]]), [N[1, 1], N[3, 3]]) # points aligned @test ispermutation(convex_hull!([N[0, 1], N[0, 2], N[0, 3]]), [N[0, 1], N[0, 3]]) # three points on a vertical line @test convex_hull!([N[0, 1], N[0, 1], N[0, 1]]) == [N[0, 1]] # three equal points + # four vertex case in 2 dimentions + A = N[1, 0] + B = N[1, 1] + C = N[-1, 1] + D = N[-1, 0] + expr = [A, B, C, D] + points = [A, B, C, D] + @test iscounterclockwise_four_points(convex_hull!(points), expr) # ABCD + points = [A, D, C, B] + @test iscounterclockwise_four_points(convex_hull!(points), expr) # ADCB + points = [A, B, D, C] + @test iscounterclockwise_four_points(convex_hull!(points), expr) # ABDC + points = [A, D, B, C] + @test iscounterclockwise_four_points(convex_hull!(points), expr) # ADBC + points = [D, B, C, A] + @test iscounterclockwise_four_points(convex_hull!(points), expr) # DBCA + points = [A, C, D, B] + @test iscounterclockwise_four_points(convex_hull!(points), expr) # ACDB + A = N[0, 1] + B = N[-1, -1] + C = N[1, -1] + D = N[0, 0] + expr = [A, B, C] + points = [A, B, C, D] + @test iscounterclockwise_three_points(convex_hull!(points), expr) # ABC + points = [A, C, B, D] + @test iscounterclockwise_three_points(convex_hull!(points), expr) # CBA + points = [D, B, C, A] + @test iscounterclockwise_three_points(convex_hull!(points), expr) # BCD + points = [D, C, B, A] + @test iscounterclockwise_three_points(convex_hull!(points), expr) # DCB + points = [B, D, C, A] + @test iscounterclockwise_three_points(convex_hull!(points), expr) # ACD + points = [C, D, B, A] + @test iscounterclockwise_three_points(convex_hull!(points), expr) # CAD + points = [B, C, D, A] + @test iscounterclockwise_three_points(convex_hull!(points), expr) # ABD + points = [C, B, D, A] + @test iscounterclockwise_three_points(convex_hull!(points), expr) # ADB + @test ispermutation(convex_hull!([N[1, 1], N[2, 2], N[3, 3], N[4, 4]]), [N[1, 1], N[4, 4]]) # points aligned + # higher dimension if test_suite_polyhedra && N != Float32 # no backend supporting Float32 points_3D = [[N(1), N(0), N(4)], [N(1), N(1), N(5)], [N(0), N(1), N(6)], From 683c47d7fd7596f29e3719c41086e299e7b776a8 Mon Sep 17 00:00:00 2001 From: Sebastian Guadalupe Date: Thu, 13 Jun 2019 22:15:52 -0300 Subject: [PATCH 02/13] solve problem in table --- src/concrete_convex_hull.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/concrete_convex_hull.jl b/src/concrete_convex_hull.jl index 9022a1d371..e454c08f54 100644 --- a/src/concrete_convex_hull.jl +++ b/src/concrete_convex_hull.jl @@ -262,7 +262,7 @@ function _four_points_2d!(points::AbstractVector{<:AbstractVector{N}}) where {N< # - + + + [should not happen] # - + + - ACD # - + - + DCB - # - + - - DBCA + # - + - - DACB # - - + + ADB # - - + - ACDB # - - - + ADCB @@ -286,9 +286,9 @@ function _four_points_2d!(points::AbstractVector{<:AbstractVector{N}}) where {N< points[1], points[2], points[3] = C, A, D # + - - + CAD pop!(points) elseif key == 1000 - # + - - - [should not happen] + @assert false "unexpected case in convex_hull" # + - - - [should not happen] elseif key == 0111 - # - + + + [should not happen] + @assert false "unexpected case in convex_hull" # - + + + [should not happen] elseif key == 0110 points[1], points[2], points[3] = A, C, D # - + + - ACD pop!(points) @@ -296,7 +296,7 @@ function _four_points_2d!(points::AbstractVector{<:AbstractVector{N}}) where {N< points[1], points[2], points[3] = D, C, B # - + - + DCB pop!(points) elseif key == 0100 - points[1], points[2], points[3], points[4] = D, B, C, A # - + - - DBCA + points[1], points[2], points[3], points[4] = D, A, C, B # - + - - DACB elseif key == 0011 points[1], points[2], points[3] = A, D, B # - - + + ADB pop!(points) From 06ce776bf351cd19f32957df3cb49c02cb9ecc76 Mon Sep 17 00:00:00 2001 From: Sebastian Guadalupe Date: Fri, 14 Jun 2019 18:25:17 -0300 Subject: [PATCH 03/13] update four points --- src/concrete_convex_hull.jl | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/concrete_convex_hull.jl b/src/concrete_convex_hull.jl index e454c08f54..0076532372 100644 --- a/src/concrete_convex_hull.jl +++ b/src/concrete_convex_hull.jl @@ -28,10 +28,9 @@ The convex hull as a list of vectors with the coordinates of the points. ### Algorithm -A pre-processing step treats the cases with `0`, `1` and `2` points for one -dimension, `0`, `1`, `2`, `3` and `4` for two dimensions, if there is more -points, its used a general algorithm for one and two dimensions. -For higher dimension there is a general algorithm. +A pre-processing step treats the cases with up to two points for one dimension +and up to four points for two dimensions. +For more points in one resp. two dimensions, we use more general algorithms. For the one-dimensional case we return the minimum and maximum points, in that order. @@ -193,10 +192,10 @@ end function _four_points_2d!(points::AbstractVector{<:AbstractVector{N}}) where {N<:Real} A, B, C, D = points[1], points[2], points[3], points[4] - tri_ABC = (A[2] - B[2]) * C[1] + (B[1] - A[1]) * C[2] + (A[1] * B[2] - B[1] * A[2]) - tri_ABD = (A[2] - B[2]) * D[1] + (B[1] - A[1]) * D[2] + (A[1] * B[2] - B[1] * A[2]) - tri_BCD = (B[2] - C[2]) * D[1] + (C[1] - B[1]) * D[2] + (B[1] * C[2] - C[1] * B[2]) - tri_CAD = (C[2] - A[2]) * D[1] + (A[1] - C[1]) * D[2] + (C[1] * A[2] - A[1] * C[2]) + tri_ABC = right_turn(A, B, C) + tri_ABD = right_turn(A, B, D) + tri_BCD = right_turn(B, C, D) + tri_CAD = right_turn(C, A, D) key = 0 if tri_ABC > zero(N) key = key + 1000 @@ -285,10 +284,6 @@ function _four_points_2d!(points::AbstractVector{<:AbstractVector{N}}) where {N< elseif key == 1001 points[1], points[2], points[3] = C, A, D # + - - + CAD pop!(points) - elseif key == 1000 - @assert false "unexpected case in convex_hull" # + - - - [should not happen] - elseif key == 0111 - @assert false "unexpected case in convex_hull" # - + + + [should not happen] elseif key == 0110 points[1], points[2], points[3] = A, C, D # - + + - ACD pop!(points) @@ -307,6 +302,8 @@ function _four_points_2d!(points::AbstractVector{<:AbstractVector{N}}) where {N< elseif key == 0000 points[1], points[2], points[3] = A, C, B # - - - - ACB pop!(points) + else + @assert false "unexpected case in convex_hull" end return points end From 3a2f95c44f564f72ca26bb4a1ad60251ab2e2a32 Mon Sep 17 00:00:00 2001 From: Sebastian Guadalupe Date: Fri, 14 Jun 2019 22:05:45 -0300 Subject: [PATCH 04/13] redefine `iscounterclockwise` --- test/unit_convex_hull.jl | 44 ++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/test/unit_convex_hull.jl b/test/unit_convex_hull.jl index a0b4c12e5c..ea82aa566a 100644 --- a/test/unit_convex_hull.jl +++ b/test/unit_convex_hull.jl @@ -38,25 +38,17 @@ for N in [Float64, Rational{Int}] convex_hull!(points_2D) # check in-place version @test points_2D == [[N(-1), N(-1)], [N(1), N(0)], [N(1), N(1)], [N(0), N(1)]] - function iscounterclockwise_four_points(result, correct_expr) - # this function checks if the result equals any of the correct answer cyclic permutation - result == correct_expr || result == circshift(correct_expr, 1) || result == circshift(correct_expr, 2) || result == circshift(correct_expr, 3) - end - - function iscounterclockwise_three_points(result, correct_expr) - # this function checks if the result equals any of the correct answer cyclic permutation - result == correct_expr || result == circshift(correct_expr, 1) || result == circshift(correct_expr, 2) - end + iscounterclockwise(result, correct_expr) = any([result == circshift(correct_expr, i) for i in 0:length(result)-1]) # three vertex case in 2 dimensions ccw_points = [N[1, 1], N[-1, 1], N[-1, 0]] ccw_p = convex_hull!(ccw_points) ccw_expr = [N[1, 1], N[-1, 1], N[-1, 0]] - @test iscounterclockwise_three_points(ccw_p, ccw_expr) # points sorted in a counter-clockwise fashion + @test iscounterclockwise(ccw_p, ccw_expr) # points sorted in a counter-clockwise fashion cw_points = [N[-1, 1], N[1, 1], N[-1, 0]] cw_p = convex_hull!(cw_points) cw_expr = [N[1, 1], N[-1, 1], N[-1, 0]] - @test iscounterclockwise_three_points(cw_p, cw_expr) # points sorted in clockwise fashion + @test iscounterclockwise(cw_p, cw_expr) # points sorted in clockwise fashion @test ispermutation(convex_hull!([N[1, 1], N[2, 2], N[3, 3]]), [N[1, 1], N[3, 3]]) # points aligned @test ispermutation(convex_hull!([N[0, 1], N[0, 2], N[0, 3]]), [N[0, 1], N[0, 3]]) # three points on a vertical line @test convex_hull!([N[0, 1], N[0, 1], N[0, 1]]) == [N[0, 1]] # three equal points @@ -68,40 +60,40 @@ for N in [Float64, Rational{Int}] D = N[-1, 0] expr = [A, B, C, D] points = [A, B, C, D] - @test iscounterclockwise_four_points(convex_hull!(points), expr) # ABCD + @test iscounterclockwise(convex_hull!(points), expr) # ABCD points = [A, D, C, B] - @test iscounterclockwise_four_points(convex_hull!(points), expr) # ADCB + @test iscounterclockwise(convex_hull!(points), expr) # ADCB points = [A, B, D, C] - @test iscounterclockwise_four_points(convex_hull!(points), expr) # ABDC + @test iscounterclockwise(convex_hull!(points), expr) # ABDC points = [A, D, B, C] - @test iscounterclockwise_four_points(convex_hull!(points), expr) # ADBC + @test iscounterclockwise(convex_hull!(points), expr) # ADBC points = [D, B, C, A] - @test iscounterclockwise_four_points(convex_hull!(points), expr) # DBCA + @test iscounterclockwise(convex_hull!(points), expr) # DBCA points = [A, C, D, B] - @test iscounterclockwise_four_points(convex_hull!(points), expr) # ACDB + @test iscounterclockwise(convex_hull!(points), expr) # ACDB A = N[0, 1] B = N[-1, -1] C = N[1, -1] D = N[0, 0] expr = [A, B, C] points = [A, B, C, D] - @test iscounterclockwise_three_points(convex_hull!(points), expr) # ABC + @test iscounterclockwise(convex_hull!(points), expr) # ABC points = [A, C, B, D] - @test iscounterclockwise_three_points(convex_hull!(points), expr) # CBA + @test iscounterclockwise(convex_hull!(points), expr) # CBA points = [D, B, C, A] - @test iscounterclockwise_three_points(convex_hull!(points), expr) # BCD + @test iscounterclockwise(convex_hull!(points), expr) # BCD points = [D, C, B, A] - @test iscounterclockwise_three_points(convex_hull!(points), expr) # DCB + @test iscounterclockwise(convex_hull!(points), expr) # DCB points = [B, D, C, A] - @test iscounterclockwise_three_points(convex_hull!(points), expr) # ACD + @test iscounterclockwise(convex_hull!(points), expr) # ACD points = [C, D, B, A] - @test iscounterclockwise_three_points(convex_hull!(points), expr) # CAD + @test iscounterclockwise(convex_hull!(points), expr) # CAD points = [B, C, D, A] - @test iscounterclockwise_three_points(convex_hull!(points), expr) # ABD + @test iscounterclockwise(convex_hull!(points), expr) # ABD points = [C, B, D, A] - @test iscounterclockwise_three_points(convex_hull!(points), expr) # ADB + @test iscounterclockwise(convex_hull!(points), expr) # ADB @test ispermutation(convex_hull!([N[1, 1], N[2, 2], N[3, 3], N[4, 4]]), [N[1, 1], N[4, 4]]) # points aligned - + # five-vertices case in 2D points = to_N(N, [[0.9, 0.2], [0.4, 0.6], [0.2, 0.1], [0.1, 0.3], [0.3, 0.28]]) points_copy = copy(points) From ae4b103f974738aa4fce6190f06ccdb4f0f10366 Mon Sep 17 00:00:00 2001 From: Sebastian Guadalupe Date: Sat, 15 Jun 2019 14:06:14 -0300 Subject: [PATCH 05/13] move iscounterclockwise to helper_functions --- src/helper_functions.jl | 16 ++++++++++++++++ test/runtests.jl | 2 +- test/unit_convex_hull.jl | 2 -- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/helper_functions.jl b/src/helper_functions.jl index 7982262cec..6b99d02fdc 100644 --- a/src/helper_functions.jl +++ b/src/helper_functions.jl @@ -645,3 +645,19 @@ A copy of the vector where each negative entry is replaced by zero. function rectify(x::AbstractVector{N}) where {N<:Real} return map(xi -> max(xi, zero(N)), x) end + +""" + iscounterclockwise(result, correct_expr) where {N<:Real} + +Returns a boolean, i.e., take the element-wise maximum with zero. + +### Input + +- `result` -- vector +- `correct_expr` -- vector with it's elements in ccw order + +### Output + +A boolean indicating if the elements of the result are in the same order as correct_expr or any of it's cyclic permutations +""" +iscounterclockwise(result::AbstractVector{N}, correct_expr::AbstractVector{N}) = any([result == circshift(correct_expr, i) for i in 0:length(result)-1]) diff --git a/test/runtests.jl b/test/runtests.jl index b5b6b68b83..a2535b166b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -11,7 +11,7 @@ using TaylorModels: set_variables, TaylorModelN include("to_N.jl") # non-exported helper functions -using LazySets: ispermutation, isinvertible, inner +using LazySets: ispermutation, isinvertible, inner, iscounterclockwise using LazySets.Approximations: UnitVector global test_suite_basic = true diff --git a/test/unit_convex_hull.jl b/test/unit_convex_hull.jl index ea82aa566a..9c8c6e788e 100644 --- a/test/unit_convex_hull.jl +++ b/test/unit_convex_hull.jl @@ -38,8 +38,6 @@ for N in [Float64, Rational{Int}] convex_hull!(points_2D) # check in-place version @test points_2D == [[N(-1), N(-1)], [N(1), N(0)], [N(1), N(1)], [N(0), N(1)]] - iscounterclockwise(result, correct_expr) = any([result == circshift(correct_expr, i) for i in 0:length(result)-1]) - # three vertex case in 2 dimensions ccw_points = [N[1, 1], N[-1, 1], N[-1, 0]] ccw_p = convex_hull!(ccw_points) From 361546b4090af7eb6e6d875bea70e365dd906981 Mon Sep 17 00:00:00 2001 From: Sebastian Guadalupe Date: Sat, 15 Jun 2019 14:11:49 -0300 Subject: [PATCH 06/13] update iscouterclockwise --- src/helper_functions.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper_functions.jl b/src/helper_functions.jl index 6b99d02fdc..2afb96bfc6 100644 --- a/src/helper_functions.jl +++ b/src/helper_functions.jl @@ -660,4 +660,4 @@ Returns a boolean, i.e., take the element-wise maximum with zero. A boolean indicating if the elements of the result are in the same order as correct_expr or any of it's cyclic permutations """ -iscounterclockwise(result::AbstractVector{N}, correct_expr::AbstractVector{N}) = any([result == circshift(correct_expr, i) for i in 0:length(result)-1]) +iscounterclockwise(result, correct_expr) = any([result == circshift(correct_expr, i) for i in 0:length(result)-1]) From 703516ac65dcc19f2a12ab10562247872ae7d2cb Mon Sep 17 00:00:00 2001 From: Sebastian Guadalupe Date: Sat, 15 Jun 2019 14:41:30 -0300 Subject: [PATCH 07/13] add iscounterclockwise to utils.md --- docs/src/lib/utils.md | 1 + src/helper_functions.jl | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/src/lib/utils.md b/docs/src/lib/utils.md index 04f07b9e24..24c6539ea3 100644 --- a/docs/src/lib/utils.md +++ b/docs/src/lib/utils.md @@ -21,6 +21,7 @@ get_radius! inner isinvertible ispermutation +iscounterclockwise issquare is_right_turn is_tighter_same_dir_2D diff --git a/src/helper_functions.jl b/src/helper_functions.jl index 2afb96bfc6..1dabe76afb 100644 --- a/src/helper_functions.jl +++ b/src/helper_functions.jl @@ -660,4 +660,6 @@ Returns a boolean, i.e., take the element-wise maximum with zero. A boolean indicating if the elements of the result are in the same order as correct_expr or any of it's cyclic permutations """ -iscounterclockwise(result, correct_expr) = any([result == circshift(correct_expr, i) for i in 0:length(result)-1]) +function iscounterclockwise(result, correct_expr) + return any([result == circshift(correct_expr, i) for i in 0:length(result)-1]) +end From fb7b4a5755bae039e1e36a0f67cd5433fadd64e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Guadalupe?= <49855687+SebastianGuadalupe@users.noreply.github.com> Date: Sat, 15 Jun 2019 15:20:15 -0300 Subject: [PATCH 08/13] Update src/helper_functions.jl Co-Authored-By: Christian Schilling --- src/helper_functions.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/helper_functions.jl b/src/helper_functions.jl index 1dabe76afb..7073413071 100644 --- a/src/helper_functions.jl +++ b/src/helper_functions.jl @@ -658,7 +658,8 @@ Returns a boolean, i.e., take the element-wise maximum with zero. ### Output -A boolean indicating if the elements of the result are in the same order as correct_expr or any of it's cyclic permutations +A boolean indicating if the elements of `result` are in the same order as +`correct_expr` or any of its cyclic permutations. """ function iscounterclockwise(result, correct_expr) return any([result == circshift(correct_expr, i) for i in 0:length(result)-1]) From 37769599550f1c5b11d01bc7b661b7535def8701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Guadalupe?= <49855687+SebastianGuadalupe@users.noreply.github.com> Date: Sat, 15 Jun 2019 15:20:27 -0300 Subject: [PATCH 09/13] Update src/helper_functions.jl Co-Authored-By: Christian Schilling --- src/helper_functions.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper_functions.jl b/src/helper_functions.jl index 7073413071..0b375290d1 100644 --- a/src/helper_functions.jl +++ b/src/helper_functions.jl @@ -654,7 +654,7 @@ Returns a boolean, i.e., take the element-wise maximum with zero. ### Input - `result` -- vector -- `correct_expr` -- vector with it's elements in ccw order +- `correct_expr` -- paragon vector with elements in ccw order ### Output From c2d902fa979faf578d516db27d49e336f89823e0 Mon Sep 17 00:00:00 2001 From: Sebastian Guadalupe Date: Sat, 15 Jun 2019 15:26:47 -0300 Subject: [PATCH 10/13] update description in docstring --- src/helper_functions.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/helper_functions.jl b/src/helper_functions.jl index 0b375290d1..8a31666a8c 100644 --- a/src/helper_functions.jl +++ b/src/helper_functions.jl @@ -649,7 +649,8 @@ end """ iscounterclockwise(result, correct_expr) where {N<:Real} -Returns a boolean, i.e., take the element-wise maximum with zero. +Returns a boolean, true if the elements in `result` are ordered in +a couter-clockwise fashion and in the same order as `correct_expr`. ### Input From 96e2a23bc7bbaf1d4beaf067e75cd7d21aac0f2f Mon Sep 17 00:00:00 2001 From: Sebastian Guadalupe Date: Sun, 16 Jun 2019 14:43:41 -0300 Subject: [PATCH 11/13] fix test --- test/unit_convex_hull.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit_convex_hull.jl b/test/unit_convex_hull.jl index 9c8c6e788e..202ac8e6c0 100644 --- a/test/unit_convex_hull.jl +++ b/test/unit_convex_hull.jl @@ -65,8 +65,8 @@ for N in [Float64, Rational{Int}] @test iscounterclockwise(convex_hull!(points), expr) # ABDC points = [A, D, B, C] @test iscounterclockwise(convex_hull!(points), expr) # ADBC - points = [D, B, C, A] - @test iscounterclockwise(convex_hull!(points), expr) # DBCA + points = [D, A, C, B] + @test iscounterclockwise(convex_hull!(points), expr) # DACB points = [A, C, D, B] @test iscounterclockwise(convex_hull!(points), expr) # ACDB A = N[0, 1] From 67bd09534cc2bd4ec7cb8bb2ee88970d50ac8476 Mon Sep 17 00:00:00 2001 From: Sebastian Guadalupe Date: Sun, 16 Jun 2019 17:31:09 -0300 Subject: [PATCH 12/13] move _collinear_case! outside _four_points_2d! --- src/concrete_convex_hull.jl | 58 ++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/concrete_convex_hull.jl b/src/concrete_convex_hull.jl index 0076532372..08a8b952d1 100644 --- a/src/concrete_convex_hull.jl +++ b/src/concrete_convex_hull.jl @@ -190,6 +190,31 @@ function _three_points_2d!(points::AbstractVector{<:AbstractVector{N}}) where {N return points end +function _collinear_case!(points::Vector{VN}, A::VN, B::VN, C::VN, D::VN)::Vector{VN} where {N<:Real, VN<:AbstractVector{N}} + # A, B and C collinear, D is the extra point + if isapprox(A[1], B[1]) && isapprox(B[1], C[1]) && isapprox(C[1], A[1]) + # points are approximately equal in their first component + if isapprox(A[2], B[2]) && isapprox(B[2], C[2]) && isapprox(C[2], A[2]) + # the three points are approximately equal + points[1], points[2] = A, D + pop!(points) + pop!(points) + return _two_points_2d!(points) + else + # assign the points with max and min value in their second component to the + # firsts points and the extra point to the third place, then pop the point that was in the middle + points[1], points[2], points[3] = points[argmin([A[2], B[2], C[2]])], points[argmax([A[2], B[2], C[2]])], D + pop!(points) + end + else + # assign the points with max and min value in their first component to the + # firsts points and the extra point to the third place, then pop the point that was in the middle + points[1], points[2], points[3] = points[argmin([A[1], B[1], C[1]])], points[argmax([A[1], B[1], C[1]])], D + pop!(points) + end + return _three_points_2d!(points) +end + function _four_points_2d!(points::AbstractVector{<:AbstractVector{N}}) where {N<:Real} A, B, C, D = points[1], points[2], points[3], points[4] tri_ABC = right_turn(A, B, C) @@ -210,42 +235,17 @@ function _four_points_2d!(points::AbstractVector{<:AbstractVector{N}}) where {N< key = key + 1 end - function collinear_case(A, B, C, D) - # A, B and C collinear, D is the extra point - if isapprox(A[1], B[1]) && isapprox(B[1], C[1]) && isapprox(C[1], A[1]) - # points are approximately equal in their first component - if isapprox(A[2], B[2]) && isapprox(B[2], C[2]) && isapprox(C[2], A[2]) - # the three points are approximately equal - points[1], points[2] = A, D - pop!(points) - pop!(points) - return _two_points_2d!(points) - else - # assign the points with max and min value in their second component to the - # firsts points and the extra point to the third place, then pop the point that was in the middle - points[1], points[2], points[3] = points[argmin([A[2], B[2], C[2]])], points[argmax([A[2], B[2], C[2]])], D - pop!(points) - end - else - # assign the points with max and min value in their first component to the - # firsts points and the extra point to the third place, then pop the point that was in the middle - points[1], points[2], points[3] = points[argmin([A[1], B[1], C[1]])], points[argmax([A[1], B[1], C[1]])], D - pop!(points) - end - return _three_points_2d!(points) - end - if isapproxzero(tri_ABC) - return collinear_case(A, B, C, D) + return _collinear_case!(points, A, B, C, D) end if isapproxzero(tri_ABD) - return collinear_case(A, B, D, C) + return _collinear_case!(points, A, B, D, C) end if isapproxzero(tri_BCD) - return collinear_case(B, C, D, A) + return _collinear_case!(points, B, C, D, A) end if isapproxzero(tri_CAD) - return collinear_case(C, A, D, B) + return _collinear_case!(points, C, A, D, B) end # ABC ABD BCD CAD hull From 3a1f983151b48ac86a93624b0f486f3919ee0cfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Guadalupe?= <49855687+SebastianGuadalupe@users.noreply.github.com> Date: Sun, 16 Jun 2019 18:03:13 -0300 Subject: [PATCH 13/13] Update src/concrete_convex_hull.jl Co-Authored-By: Christian Schilling --- src/concrete_convex_hull.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/concrete_convex_hull.jl b/src/concrete_convex_hull.jl index 08a8b952d1..13457884f1 100644 --- a/src/concrete_convex_hull.jl +++ b/src/concrete_convex_hull.jl @@ -190,7 +190,7 @@ function _three_points_2d!(points::AbstractVector{<:AbstractVector{N}}) where {N return points end -function _collinear_case!(points::Vector{VN}, A::VN, B::VN, C::VN, D::VN)::Vector{VN} where {N<:Real, VN<:AbstractVector{N}} +function _collinear_case!(points::Vector{VN}, A::VN, B::VN, C::VN, D::VN) where {N<:Real, VN<:AbstractVector{N}} # A, B and C collinear, D is the extra point if isapprox(A[1], B[1]) && isapprox(B[1], C[1]) && isapprox(C[1], A[1]) # points are approximately equal in their first component