Skip to content

Commit

Permalink
Add density methods (#17)
Browse files Browse the repository at this point in the history
* adds basic density methods (DTFE, WeightedDTFE)

* adds test for dtfe and wdtfe local density methods

test on push/mr to dev
  • Loading branch information
harveydevereux committed Dec 19, 2023
1 parent 5a499da commit 5ac99a6
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 4 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ on:
pull_request:
branches:
- master
- dev
push:
branches:
- master
- dev
tags: '*'
jobs:
test:
Expand Down
9 changes: 6 additions & 3 deletions src/AlphaShapes.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module AlphaShapes
import BlackBoxOptim.bboptimize, BlackBoxOptim.best_candidate
import Distances.pairwise, Distances.Euclidean
import LinearAlgebra.det, LinearAlgebra.inv
import LinearAlgebra.det, LinearAlgebra.inv, LinearAlgebra.norm

using MiniQhull

Expand Down Expand Up @@ -123,15 +123,15 @@ julia>:([0 1 1 1
Wrap MiniQhull.jl's delaunay to get a delaunay triangualation in any
dimension
"""
function GetDelaunayTriangulation(points::AbstractArray{Float64,2})::AbstractArray{Float64,3}
function GetDelaunayTriangulation(points::AbstractArray{Float64,2}, indices::Bool=false)
tess = delaunay(points)
Triangles = zeros(size(tess,1)-1,size(tess,1),size(tess,2))
for i in axes(tess, 1)
for j in axes(tess, 2)
view(Triangles, :, i, j) .= view(points, :, tess[i, j])
end
end
return Triangles
return indices ? (Triangles, tess) : Triangles
end

"""
Expand Down Expand Up @@ -198,4 +198,7 @@ julia>:([0 1 1 1
end
return T[:,:,A]
end

include("utils.jl")
include("density.jl")
end # module AlphaShapes
79 changes: 79 additions & 0 deletions src/density.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""
ContiguousVoronoiCell(point_index::Int,tess,tess_inds)
For a data point (point_index) return the simplices which share this
point as a common vertex
"""
function ContiguousVoronoiCell(point_index::Int,tess,tess_inds)
tris = []
pos = []
for ind in findall(x->x.==point_index,tess_inds)
push!(tris,ind[2])
push!(pos,ind[1])
end
return tess[tris,:,:],pos
end

"""
ContiguousVoronoiCellArea(point_index::Int,tess,tess_inds)
For a data point (point_index) return the sum of the areas of the simplices
which share this point as a common vertex
"""
function ContiguousVoronoiCellArea(point_index::Int,tess,tess_inds)
tris,inds = ContiguousVoronoiCell(point_index,tess,tess_inds)
A = 0.0
for i in 1:size(tris,1)
A += AlphaShapes.SimplexVolume(tris[i,:,:])
end
return A
end

"""
DTFELocalDensity(point_index::Int,tess,tess_inds)
Return the local desnity at a point in the Delaunay Tesselation Field
Estimator sense.
https://arxiv.org/pdf/0708.1441.pdf
"""
DTFELocalDensity(point_index::Int,tess,tess_inds)::Float64 = size(tess,2)/ContiguousVoronoiCellArea(point_index,tess,tess_inds)

function LawOfCosine(a::Float64,b::Float64,c::Float64)::Float64
return acos(max(-1.0,min(1.0,(a^2 + b^2 - c^2) / (2*a*b+1e-100))))
end

function TriangleAngles(triangle::Array{Float64,2})::Array{Float64,1}
p_a, p_b, p_c = triangle[1,:],triangle[2,:],triangle[3,:]
a = norm(p_c .- p_b)
b = norm(p_a .- p_c)
c = norm(p_a .- p_b)

A = LawOfCosine(b,c,a)
B = LawOfCosine(a,c,b)
C = π - (A + B)
return [A,B,C]
end

PointWeights(triangle::Array{Float64,2})::Array{Float64,1} = TriangleAngles(triangle) ./ 2π

"""
WeightedDTFELocalDensity(point_index::Int,tess,tess_inds)
Return the local desnity at a point in the Weighted Delaunay Tesselation Field
Estimator sense. This additionally normalises by the 'contribution' of individual
points (portion of 2pi rad. in triangle)
https://royalsocietypublishing.org/doi/10.1098/rsif.2021.0114
"""

function WeightedDTFELocalDensity(point_index::Int,tess,tess_inds)::Float64
tris,pos = ContiguousVoronoiCell(point_index,tess,tess_inds)
A = zeros(size(tris,1))
w = zeros(size(tris,1))
for i in 1:size(tris,1)
w[i] = PointWeights(tris[i,:,:])[pos[i]]
A[i] = AlphaShapes.SimplexVolume(tris[i,:,:])
end
return sum(w./A)/2.0*π
end
3 changes: 2 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ using Test
my_tests = [
"test_circum_sphere.jl",
"test_simplex_volume.jl",
"test_alpha_shape.jl"
"test_alpha_shape.jl",
"test_density.jl"
]

println("Running tests:")
Expand Down
25 changes: 25 additions & 0 deletions test/test_density.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using AlphaShapes
using Test

@testset "Test Local Density" begin
# four corners
X = [0.0 0.0 1.0 1.0; 0.0 1.0 1.0 0.0]
tess, inds = AlphaShapes.GetDelaunayTriangulation(X,true)
density = [AlphaShapes.WeightedDTFELocalDensity(i,tess,inds) for i in 1:size(X,2)]

@test density == [
0.39269908169872425,
0.39269908169872403,
0.39269908169872425,
0.39269908169872403
]

density = [AlphaShapes.DTFELocalDensity(i,tess,inds) for i in 1:size(X,2)]

@test density == [
3.0,
1.5,
3.0,
1.5
]
end

0 comments on commit 5ac99a6

Please sign in to comment.