Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve RefMeshes API #87

Merged
merged 10 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
Meshes = "eacbb407-ea5a-433e-ab97-5258b1ca43fa"
MultiScaleTreeGraph = "dd4a991b-8a45-4075-bede-262ee62d5583"
Observables = "510215fc-4207-5dde-b226-833fc4488ee2"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
Rotations = "6038ab10-8711-5258-84ad-4b1120ba62dc"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
Expand All @@ -28,6 +29,7 @@ Makie = "0.21"
Meshes = "0.52"
MultiScaleTreeGraph = "0.14"
Observables = "0.5"
OrderedCollections = "1.7.0"
RecipesBase = "1"
Rotations = "1"
StaticArrays = "1"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The package provides different functionalities, the main ones being:
- plotting using `viz` and `viz!`, optionally using coloring by attribute;
- mesh transformations using `transform_mesh!`

Note that `:geometry` is a reserved attribute in nodes (*e.g.* organs) used for the 3D geometry. It is stored as a special structure (`geometry`).
Note that `:geometry` is a reserved attribute in nodes (*e.g.* organs) used for the 3D geometry. It is stored as a special structure (`Geometry`).

## Example usage

Expand Down
4 changes: 2 additions & 2 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The package provides different functionalities, the main ones being:
- plotting using `viz` and `viz!`, optionally using coloring by attribute;
- mesh transformations using [`transform_mesh!`](@ref)

Note that PlantGeom reserves the `:geometry` attribute in the nodes (*e.g.* organs). It uses it to store the 3D geometry as a special structure ([`geometry`](@ref)).
Note that PlantGeom reserves the `:geometry` attribute in the nodes (*e.g.* organs). It uses it to store the 3D geometry as a special structure ([`Geometry`](@ref)).

```@setup animation
using CairoMakie, Meshes, PlantGeom, MultiScaleTreeGraph # Note: CairoMakie must be loaded before PlantGeom to access the extensions
Expand Down Expand Up @@ -63,7 +63,7 @@ opf = read_opf(joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","simp
transform!(opf, refmesh_to_mesh!)
# And compute the max z of each node based on their mesh:
transform!(opf, zmax => :z_node, ignore_nothing = true)
# Or the z coordinate of each vertez of each node mesh:
# Or the z coordinate of each vertex of each node mesh:
transform!(opf, :geometry => (x -> [Meshes.coords(i)z for i in Meshes.vertices(x.mesh)]) => :z_vertex, ignore_nothing = true)


Expand Down
8 changes: 4 additions & 4 deletions docs/src/makie_3d.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,18 +114,18 @@ We can get the default color of each reference mesh by using:
get_ref_meshes_color(ref_meshes)
```

Now we know the first reference mesh is the cylinder (it is brown) and the second one is the leaf (it is green).
Now we know there are two reference meshes, one for a cylinder called "Mesh0" colored in brown, and a second one for the leaf that is green.

To update their colors we can simply pass the new colors as a dictionary mapping colors to reference meshes like so:
To update their colors we can simply pass the new colors as a dictionary mapping colors to the names of the reference meshes like so:

```@example 2
viz(mtg, color = Dict(1 => :gray87, 2 => "#42A25ABD"))
viz(mtg, color = Dict("Mesh0" => :gray87, "Mesh1" => "#42A25ABD"))
```

If we want to update the second reference mesh only (the leaves), we would do:

```@example 2
viz(mtg, color = Dict(2 => "#42A25ABD"))
viz(mtg, color = Dict("Mesh1" => "#42A25ABD"))
```

### Map color to attributes
Expand Down
14 changes: 7 additions & 7 deletions ext/makie_recipes/RefMeshes_recipes.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Makie.plottype(::RefMeshes) = MeshesMakieExt.Viz{<:Tuple{RefMeshes}}
Makie.args_preferred_axis(::RefMeshes) = Makie.LScene
Makie.plottype(::Vector{RefMesh}) = MeshesMakieExt.Viz{<:Tuple{Vector{RefMesh}}}
Makie.args_preferred_axis(::Vector{RefMesh}) = Makie.LScene

# Documentation is in opf_recipe.jl
function Makie.plot!(plot::MeshesMakieExt.Viz{<:Tuple{RefMeshes}})
function Makie.plot!(plot::MeshesMakieExt.Viz{<:Tuple{Vector{RefMesh}}})
# Mesh list:
p = PlantGeom.align_ref_meshes(plot[:object][])

Expand All @@ -18,7 +18,7 @@ function Makie.plot!(plot::MeshesMakieExt.Viz{<:Tuple{RefMeshes}})
# see here for default value in MeshViz:
# https://github.com/JuliaGeometry/MeshViz.jl/blob/6e37908c78c06212f09229e3e8d92483535ffa16/src/MeshViz.jl#L50
ref_colors = get_ref_meshes_color(plot[:object][])
colorant = Observables.Observable(Dict(zip(1:n_meshes, ref_colors)))
colorant = Observables.Observable(ref_colors)
else
colorant = Makie.lift(x -> Dict(zip(keys(p), repeat([x], n_meshes))), color)
end
Expand All @@ -34,7 +34,7 @@ function Makie.plot!(plot::MeshesMakieExt.Viz{<:Tuple{RefMeshes}})
end

# Parsing the colors in the dictionary into Colorants:
new_color = Dict{Int,Union{Colorant,Vector{<:Colorant}}}([k => isa(v, AbstractArray) ? parse.(Colorant, v) : parse(Colorant, v) for (k, v) in colorant[]])
new_color = Dict{String,Union{Colorant,Vector{<:Colorant}}}([k => isa(v, AbstractArray) ? parse.(Colorant, v) : parse(Colorant, v) for (k, v) in colorant[]])

if length(colorant[]) != n_meshes
ref_cols = get_ref_meshes_color(plot[:object][])
Expand All @@ -44,7 +44,7 @@ function Makie.plot!(plot::MeshesMakieExt.Viz{<:Tuple{RefMeshes}})
end
end

for (key, value) in enumerate(p)
MeshesMakieExt.viz!(plot, value, color=new_color[key], segmentcolor=plot[:segmentcolor], showsegments=plot[:showsegments], colormap=plot[:colormap])
for (name, refmesh) in p
MeshesMakieExt.viz!(plot, refmesh, color=new_color[name], segmentcolor=plot[:segmentcolor], showsegments=plot[:showsegments], colormap=plot[:colormap])
end
end
15 changes: 7 additions & 8 deletions ext/makie_recipes/opf/plot_opf.jl
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,6 @@ function plot_opf(plot)
link = hasproperty(plot, :link) ? plot[:link][] : nothing

plot_opf(colorant, plot, f, symbol, scale, link)
#? NB: implement scale / symbol / link / filter_fun filtering to be able to plot only
#? a subset of the plant/scene. This will be especially usefull when we have different
#? kind of geometries at different scales of representation.
end

# Case where the color is a colorant (e.g. `:red`, or `RGB(0.1,0.5,0.1)`):
Expand Down Expand Up @@ -106,14 +103,12 @@ function plot_opf(colorant::Observables.Observable{T}, plot, f, symbol, scale, l

opf = plot[:object]

ref_meshes = get_ref_meshes(opf[])

any_node_selected = Ref(false)
# Make the plot, case where the color is a color for each reference mesh:
MultiScaleTreeGraph.traverse!(opf[]; filter_fun=f, symbol=symbol, scale=scale, link=link) do node
any_node_selected[] = true

node[color_attr_name] = Makie.@lift color_from_refmeshes($colorant, node, ref_meshes)
node[color_attr_name] = Makie.@lift color_from_refmeshes($colorant, node)
MeshesMakieExt.viz!(
plot,
node[:geometry].mesh === nothing ? refmesh_to_mesh(node) : node[:geometry].mesh,
Expand All @@ -128,8 +123,12 @@ function plot_opf(colorant::Observables.Observable{T}, plot, f, symbol, scale, l
any_node_selected[] || error("No corresponding node found for the selection given as the combination of `symbol`, `scale`, `link` and `filter_fun` arguments. ")
end

function color_from_refmeshes(color::Union{RefMeshColorant,DictRefMeshColorant,DictVertexRefMeshColorant}, node, ref_meshes)
color.colors[PlantGeom.get_ref_mesh_index!(node, ref_meshes)]
function color_from_refmeshes(color::RefMeshColorant, node)
material_single_color(node.geometry.ref_mesh.material)
end

function color_from_refmeshes(color::Union{DictRefMeshColorant,DictVertexRefMeshColorant}, node)
get(color.colors, get_ref_mesh_name(node), material_single_color(node.geometry.ref_mesh.material))
end

# Case where the color is an attribute of the MTG:
Expand Down
4 changes: 3 additions & 1 deletion src/PlantGeom.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import StaticArrays: SMatrix, SVector
import LinearAlgebra: I, UniformScaling, Diagonal # Used for geometry parsing in OPF
import RecipesBase
import Base
import OrderedCollections

# For random name of the color attribute for caching:
import UUIDs
Expand All @@ -52,6 +53,7 @@ include("colors/colors.jl")
include("colors/get_mtg_color.jl")
include("opf/mtg_recipe_helpers.jl")
include("opf/diagram.jl")
include("deprecated.jl")

# function viz2 end
# function viz2! end
Expand All @@ -71,7 +73,7 @@ export xmax, ymax, zmax, xmin, ymin, zmin
export refmesh_to_mesh!
export transform_mesh!
export Material, Phong
export RefMesh, RefMeshes
export RefMesh
export (==), names
export get_color, get_colormap

Expand Down
42 changes: 5 additions & 37 deletions src/colors/get_mtg_color.jl
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
struct RefMeshColorant
colors::Vector{Colorant}
end
struct RefMeshColorant end

struct AttributeColorant
color::Symbol
end

struct DictRefMeshColorant
colors::Dict{Int64,Colorant}
colors::Dict{String,Colorant}
end

struct DictVertexRefMeshColorant
colors::Dict{Int64,Union{Colorant,Vector{<:Colorant}}}
colors::Dict{String,Union{Colorant,Vector{<:Colorant}}}
end

struct VectorColorant
Expand Down Expand Up @@ -52,7 +50,7 @@ function get_mtg_color(color, opf)
end

function get_mtg_color(::Type{RefMeshColorantType}, color, opf)
return RefMeshColorant(get_ref_meshes_color(get_ref_meshes(opf)))
return RefMeshColorant()
end

function get_mtg_color(::Type{AttributeColorantType}, color, opf)
Expand All @@ -64,42 +62,12 @@ function get_mtg_color(::Type{T}, color, opf) where {T<:Symbol}
end

function get_mtg_color(::Type{DictRefMeshColorantType}, color, opf)
ref_meshes = get_ref_meshes(opf)

# Parsing the colors in the dictionary into Colorants:
new_color = Dict{Int,Colorant}([k => parse(Colorant, v) for (k, v) in color])

# If color is a dictionary, it should have the same length as number of RefMeshes, or
# if not, we provide the missing values from the reference mesh color:
if length(color) != length(ref_meshes.meshes)
ref_cols = get_ref_meshes_color(ref_meshes)
missing_mesh_input = setdiff(collect(keys(ref_cols)), collect(keys(color)))
for i in missing_mesh_input
push!(new_color, i => ref_cols[i])
end
end

new_color = Dict{String,Colorant}([k => parse(Colorant, v) for (k, v) in color])
return DictRefMeshColorant(new_color)
end

function get_mtg_color(::Type{DictVertexRefMeshColorantType}, color, opf)
ref_meshes = get_ref_meshes(opf)

# If color is a dictionary, it should have the same length as number of RefMeshes, or
# if not, we provide the missing values from the reference mesh color:
if length(color) != length(ref_meshes.meshes)
# Parsing the colors in the dictionary into Colorants:
new_color = Dict{Int,Union{Colorant,Vector{<:Colorant}}}(
[k => (isa(v, AbstractVector) ? parse.(Colorant, v) : parse(Colorant, v)) for (k, v) in color]
)
ref_cols = get_ref_meshes_color(ref_meshes)
missing_mesh_input = setdiff(collect(keys(ref_cols)), collect(keys(color)))
for i in missing_mesh_input
push!(new_color, i => ref_cols[i])
end
color = new_color
end

return DictVertexRefMeshColorant(color)
end

Expand Down
1 change: 1 addition & 0 deletions src/deprecated.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@deprecate geometry(; ref_mesh, ref_mesh_index=nothing, transformation=Identity(), dUp=1.0, dDwn=1.0, mesh=nothing) Geometry(; ref_mesh, transformation=Identity(), dUp=1.0, dDwn=1.0, mesh=nothing)
18 changes: 3 additions & 15 deletions src/equality.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,12 @@ function Base.:(==)(a::T, b::T) where {T<:RefMesh}
end

"""
==(a::RefMeshes, b::RefMeshes)
==(a::Geometry, b::Geometry)

Test RefMeshes equality.
"""
function Base.:(==)(a::T, b::T) where {T<:RefMeshes}
isequal(names(a), names(b)) &&
all([isequal(a.meshes[i], b.meshes[i]) for i in 1:length(a.meshes)])
end


"""
==(a::geometry, b::geometry)

Test RefMeshes equality.
Test RefMesh equality.
"""
function Base.:(==)(a::T, b::T) where {T<:geometry}
function Base.:(==)(a::T, b::T) where {T<:Geometry}
isequal(a.ref_mesh, b.ref_mesh) &&
isequal(a.ref_mesh_index, b.ref_mesh_index) &&
isequal(a.transformation(Meshes.Point(1, 2, 3)), b.transformation(Meshes.Point(1, 2, 3))) &&
# NB: transform a point here because transformations can't be compared directly
isequal(a.dUp, b.dUp) &&
Expand Down
24 changes: 2 additions & 22 deletions src/helpers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function Meshes.nvertices(mesh::RefMesh)
end

"""
nelements(meshes::RefMeshes)
nelements(meshes::RefMesh)

Return the number of elements of a reference mesh
"""
Expand Down Expand Up @@ -51,24 +51,4 @@ function normals_vertex(mesh::Meshes.SimpleMesh)
end

return SVector{length(vertex_normals)}(vertex_normals)
end

"""
nvertices(meshes::RefMeshes)

Return the number of vertices for each reference mesh as a vector of nvertices
"""
function Meshes.nvertices(meshes::RefMeshes)
[Meshes.nvertices(i) for i in meshes.meshes]
# TODO: Implement for RefMesh with GeometryBasics
end

"""
nelements(meshes::RefMeshes)

Return the number of elements for each reference mesh as a vector of nelements
"""
function Meshes.nelements(meshes::RefMeshes)
[Meshes.nelements(i) for i in meshes.meshes]
# TODO: Implement for RefMesh with GeometryBasics
end
end
6 changes: 0 additions & 6 deletions src/meshes/transformations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,4 @@ end

function apply(t, x::RefMesh)
RefMesh(x.name, apply(t, x.mesh), x.normals, x.texture_coords, x.material, x.taper)
end

function apply(t, x::RefMeshes)
for i in eachindex(x)
x[i] = apply(t, x[i])
end
end
5 changes: 2 additions & 3 deletions src/opf/read_opf.jl
Original file line number Diff line number Diff line change
Expand Up @@ -415,9 +415,8 @@ function parse_opf_topology!(node, mtg, features, attr_type, mtg_type, ref_meshe

push!(
attrs,
Symbol(elem.name) => geometry(
ref_meshes.meshes[geom[:shapeIndex]],
geom[:shapeIndex],
Symbol(elem.name) => Geometry(
ref_meshes[geom[:shapeIndex]],
transformation,
geom[:dUp],
geom[:dDwn],
Expand Down
Loading
Loading