I’m trying to create a 3d plot of some brain regions. However, the data I have is not a mesh but a series of 3d points that indicate the exterior border of the regions. How can I plot this with GLMakie?
Below is the code I’m using. The two data files, ROI_MNI_V4_List.mat
and ROI_MNI_V4_Border.mat
can be obtained from EBRAINS - Knowledge Graph Search and then going to Get data
.
using GLMakie
import Colors, GeometryBasics
import MAT # for reading matlab data files
# [5ae59095] Colors v0.12.10
# [e9467ef8] GLMakie v0.8.6
# [5c1252a2] GeometryBasics v0.4.7
# [23992714] MAT v0.10.5
# wherever you downloaded the aal atlas
root = joinpath(pwd(), "aal_for_SPM12")
roi_list = MAT.matread(joinpath(root, "ROI_MNI_V4_List.mat"))
roi_border = MAT.matread(joinpath(root, "ROI_MNI_V4_Border.mat"))
distinct_ids = unique(vec(Int.(roi_list["ROI"]["ID"])))
ids = eachindex(distinct_ids)
ids_indices = [findall(==(distinct_ids[id]), vec(roi_border["BORDER_V"])) for id in ids]
# a vector with the boundary regions for each id
border_points = [
GeometryBasics.Point3f.(eachcol(view(roi_border["BORDER_XYZ"], :, indices)))
for indices in ids_indices
]
mesh(border_points[1]) # visualize only the first region for now
The code above sort of plots something okay, but the mesh is not a volume but a disjoint set of triangles.
I tried a bunch of things, but I also noticed that e.g., GeometryBasics.mesh
fails completely for this type of data and returns an empty Mesh:
julia> GeometryBasics.mesh(border_points[1])
Mesh{3, Float32, Triangle}
I figured that the border points essentially represent a 3d polygon, but that doesn’t help much either:
julia> polygon = GeometryBasics.Polygon(border_points[1])
julia> GeometryBasics.mesh(polygon)
ERROR: MethodError: no method matching earcut_triangulate(::Vector{Vector{Point{3, Float32}}})
Closest candidates are:
earcut_triangulate(::Vector{Vector{Point2{Float64}}})
@ GeometryBasics ~/.julia/packages/GeometryBasics/Du43H/src/triangulation.jl:181
earcut_triangulate(::Vector{Vector{Point{2, Float32}}})
@ GeometryBasics ~/.julia/packages/GeometryBasics/Du43H/src/triangulation.jl:189
earcut_triangulate(::Vector{Vector{Point2{Int64}}})
@ GeometryBasics ~/.julia/packages/GeometryBasics/Du43H/src/triangulation.jl:197
...
Stacktrace:
[1] faces(polygon::GeometryBasics.Polygon{3, Float32, Point{3, Float32}, GeometryBasics.LineString{3, Float32, Point{3, Float32}, Base.ReinterpretArray{GeometryBasics.Line{3, Float32}, 1, Tuple{Point{3, Float32}, Point{3, Float32}}, GeometryBasics.TupleView{Tuple{Point{3, Float32}, Point{3, Float32}}, 2, 1, Vector{Point{3, Float32}}}, false}}, Vector{GeometryBasics.LineString{3, Float32, Point{3, Float32}, Base.ReinterpretArray{GeometryBasics.Line{3, Float32}, 1, Tuple{Point{3, Float32}, Point{3, Float32}}, GeometryBasics.TupleView{Tuple{Point{3, Float32}, Point{3, Float32}}, 2, 1, Vector{Point{3, Float32}}}, false}}}})
@ GeometryBasics ~/.julia/packages/GeometryBasics/Du43H/src/triangulation.jl:224
[2] decompose(#unused#::Type{GeometryBasics.NgonFace{3, GeometryBasics.OffsetInteger{-1, UInt32}}}, primitive::GeometryBasics.Polygon{3, Float32, Point{3, Float32}, GeometryBasics.LineString{3, Float32, Point{3, Float32}, Base.ReinterpretArray{GeometryBasics.Line{3, Float32}, 1, Tuple{Point{3, Float32}, Point{3, Float32}}, GeometryBasics.TupleView{Tuple{Point{3, Float32}, Point{3, Float32}}, 2, 1, Vector{Point{3, Float32}}}, false}}, Vector{GeometryBasics.LineString{3, Float32, Point{3, Float32}, Base.ReinterpretArray{GeometryBasics.Line{3, Float32}, 1, Tuple{Point{3, Float32}, Point{3, Float32}}, GeometryBasics.TupleView{Tuple{Point{3, Float32}, Point{3, Float32}}, 2, 1, Vector{Point{3, Float32}}}, false}}}})
@ GeometryBasics ~/.julia/packages/GeometryBasics/Du43H/src/interfaces.jl:103
[3] decompose_triangulate_fallback(primitive::GeometryBasics.Polygon{3, Float32, Point{3, Float32}, GeometryBasics.LineString{3, Float32, Point{3, Float32}, Base.ReinterpretArray{GeometryBasics.Line{3, Float32}, 1, Tuple{Point{3, Float32}, Point{3, Float32}}, GeometryBasics.TupleView{Tuple{Point{3, Float32}, Point{3, Float32}}, 2, 1, Vector{Point{3, Float32}}}, false}}, Vector{GeometryBasics.LineString{3, Float32, Point{3, Float32}, Base.ReinterpretArray{GeometryBasics.Line{3, Float32}, 1, Tuple{Point{3, Float32}, Point{3, Float32}}, GeometryBasics.TupleView{Tuple{Point{3, Float32}, Point{3, Float32}}, 2, 1, Vector{Point{3, Float32}}}, false}}}}; pointtype::Type, facetype::Type)
@ GeometryBasics ~/.julia/packages/GeometryBasics/Du43H/src/meshes.jl:91
[4] decompose_triangulate_fallback
@ ~/.julia/packages/GeometryBasics/Du43H/src/meshes.jl:89 [inlined]
[5] mesh(polygon::GeometryBasics.Polygon{3, Float32, Point{3, Float32}, GeometryBasics.LineString{3, Float32, Point{3, Float32}, Base.ReinterpretArray{GeometryBasics.Line{3, Float32}, 1, Tuple{Point{3, Float32}, Point{3, Float32}}, GeometryBasics.TupleView{Tuple{Point{3, Float32}, Point{3, Float32}}, 2, 1, Vector{Point{3, Float32}}}, false}}, Vector{GeometryBasics.LineString{3, Float32, Point{3, Float32}, Base.ReinterpretArray{GeometryBasics.Line{3, Float32}, 1, Tuple{Point{3, Float32}, Point{3, Float32}}, GeometryBasics.TupleView{Tuple{Point{3, Float32}, Point{3, Float32}}, 2, 1, Vector{Point{3, Float32}}}, false}}}}; pointtype::Type, facetype::Type, normaltype::Nothing)
@ GeometryBasics ~/.julia/packages/GeometryBasics/Du43H/src/meshes.jl:161
[6] mesh(polygon::GeometryBasics.Polygon{3, Float32, Point{3, Float32}, GeometryBasics.LineString{3, Float32, Point{3, Float32}, Base.ReinterpretArray{GeometryBasics.Line{3, Float32}, 1, Tuple{Point{3, Float32}, Point{3, Float32}}, GeometryBasics.TupleView{Tuple{Point{3, Float32}, Point{3, Float32}}, 2, 1, Vector{Point{3, Float32}}}, false}}, Vector{GeometryBasics.LineString{3, Float32, Point{3, Float32}, Base.ReinterpretArray{GeometryBasics.Line{3, Float32}, 1, Tuple{Point{3, Float32}, Point{3, Float32}}, GeometryBasics.TupleView{Tuple{Point{3, Float32}, Point{3, Float32}}, 2, 1, Vector{Point{3, Float32}}}, false}}}})
@ GeometryBasics ~/.julia/packages/GeometryBasics/Du43H/src/meshes.jl:158
[7] top-level scope
For what it’s worth, if I visualize all regions then this does produce some rather artsy figures:
but as soon as you look at a different angle it’s clear that everything is messed up
Does anybody have any ideas on how to fix this?