API
PlantGeom.ExtrudedTubeGeometryPlantGeom.GenericGeometryJobPlantGeom.GeometryPlantGeom.MaterialPlantGeom.PhongPlantGeom.RefMeshPlantGeom.StaticGeometryJobBase.:(==)Base.:(==)PlantGeom._normalize_node_symbolPlantGeom._normalized_ref_meshes_dictPlantGeom.align_ref_meshesPlantGeom.attributes_to_xmlPlantGeom.build_merged_mesh_with_mapPlantGeom.build_merged_mesh_with_map_threadedPlantGeom.bump_scene_version!PlantGeom.circle_section_profilePlantGeom.color_typePlantGeom.colorant_to_stringPlantGeom.coordinates!PlantGeom.default_amap_geometry_conventionPlantGeom.diagramPlantGeom.diagram!PlantGeom.extend_posPlantGeom.extrude_profile_meshPlantGeom.extrude_profile_refmeshPlantGeom.extrude_tube_meshPlantGeom.extrusion_make_curvePlantGeom.extrusion_make_interpolationPlantGeom.extrusion_make_pathPlantGeom.extrusion_make_splinePlantGeom.geometry_from_attributesPlantGeom.geometry_to_meshPlantGeom.get_attr_typePlantGeom.get_cached_scenePlantGeom.get_colorPlantGeom.get_color_rangePlantGeom.get_colormapPlantGeom.get_mtg_colorPlantGeom.get_ref_mesh_namePlantGeom.get_ref_meshesPlantGeom.get_ref_meshes_colorPlantGeom.has_geometryPlantGeom.lathe_gen_meshPlantGeom.lathe_gen_refmeshPlantGeom.lathe_meshPlantGeom.lathe_refmeshPlantGeom.leaflet_midrib_profilePlantGeom.map_coordPlantGeom.materialBDD_to_materialPlantGeom.material_to_opf_stringPlantGeom.merge_simple_meshesPlantGeom.meshBDD_to_meshesPlantGeom.mtg_coordinates_dfPlantGeom.mtg_to_opf_linkPlantGeom.mtg_topology_to_xml!PlantGeom.nelementsPlantGeom.normals_vertexPlantGeom.nverticesPlantGeom.parse_geometryPlantGeom.parse_materialBDD!PlantGeom.parse_meshBDD!PlantGeom.parse_opf_arrayPlantGeom.parse_opf_attributeBDD!PlantGeom.parse_opf_elements!PlantGeom.parse_opf_topology!PlantGeom.parse_ref_meshesPlantGeom.plantvizPlantGeom.plantviz!PlantGeom.read_opfPlantGeom.read_opsPlantGeom.read_ops_filePlantGeom.reconstruct_geometry_from_attributes!PlantGeom.refmesh_to_meshPlantGeom.rotate_pointPlantGeom.scene_cache_keyPlantGeom.scene_versionPlantGeom.set_cached_scene!PlantGeom.set_geometry_from_attributes!PlantGeom.taperPlantGeom.transform_mesh!PlantGeom.transformation_from_attributesPlantGeom.write_gwaPlantGeom.write_opsPlantGeom.write_opsPlantGeom.write_ops_filePlantGeom.xmaxPlantGeom.xminPlantGeom.ymaxPlantGeom.yminPlantGeom.zmaxPlantGeom.zminRecipesBase.plotRecipesBase.plot!
PlantGeom.ExtrudedTubeGeometry — Type
ExtrudedTubeGeometry(path;
n_sides=8,
radius=0.5,
radii=nothing,
widths=nothing,
heights=nothing,
path_normals=nothing,
torsion=true,
cap_ends=false,
material=RGB(220 / 255, 220 / 255, 220 / 255),
transformation=IdentityTransformation())Procedural geometry source backed by extrude_tube_mesh.
Unlike Geometry, this source does not reference a pre-existing RefMesh; the mesh is rebuilt from its path/section parameters whenever materialized (for example by scene merging).
PlantGeom.GenericGeometryJob — Type
GenericGeometryJobTyped fallback job for additional geometry source types materialized through geometry_to_mesh(geom).
PlantGeom.Geometry — Type
Geometry(; ref_mesh<:RefMesh, transformation=IdentityTransformation(), dUp=1.0, dDwn=1.0)A Node geometry with the reference mesh, its transformation (as a function) and the resulting mesh (optional, may be lazily computed).
The transformation field should be a CoordinateTransformations.Transformation, such as IdentityTransformation, Translation, LinearMap or AffineMap.
PlantGeom.Material — Type
A material for the illumination model (e.g. Phong illumination).
PlantGeom.Phong — Type
Data structure for a mesh material that is used to describe the light components of a Phong reflection type model. All data is stored as RGBα for Red, Green, Blue and transparency.
PlantGeom.RefMesh — Type
RefMesh(
name::S
mesh::SimpleMesh
normals::N
texture_coords::T
material::M
taper::Bool
)
RefMesh(name, mesh, material = RGB(220 / 255, 220 / 255, 220 / 255))RefMesh type. Stores all information about a Mesh:
name::S: the mesh namemesh::SimpleMesh: the actual mesh information -> points and topologynormals::Vector{Float64}: the normals, given as a vector of x1,y1,z1,x2,y2,z2...texture_coords::Vector{Float64}: the texture coordinates (not used yet), idem, a vectormaterial::M: the material, used to set the shadingtaper::Bool:trueif tapering is enabled
The reference meshes are then transformed on each node of the MTG using a transformation matrix to match the actual mesh.
PlantGeom.StaticGeometryJob — Type
StaticGeometryJobTyped scene-materialization job for classic RefMesh + transformation geometries.
Base.:(==) — Method
==(a::Geometry, b::Geometry)Test RefMesh equality.
Base.:(==) — Method
==(a::RefMesh, b::RefMesh)Test RefMesh equality.
PlantGeom._normalize_node_symbol — Method
merge_children_geometry!(mtg; from, into, delete=:nodes, verbose=true, child_link_fun=x -> new_child_link(x, verbose))Simplifies the geometry of a MultiScaleTreeGraph (MTG) by merging low-scale geometries into an higher-scale geometry.
Arguments
mtg: The MultiScaleTreeGraph to process.from: The symbol for the type of nodes to simplify. Can be a symbol/string or a vector/tuple of those, e.g.[:Petiole, :Rachis].into: The symbol for the type of nodes to merge into. Must be a single symbol/string, e.g.:Leaf.delete: A symbol indicating whether to delete the nodes or the geometry after merging::none: No deletion will be performed, the geometry is merged into theintonodes, and also kept as before in thefromnodes.:nodes: The nodes of typefromwill be deleted after merging.:geometry: Only the geometry will be deleted, but thefromnodes will remain in the MTG.
verbose: A boolean indicating if information should be returned when nodes or geometry was not found on expected nodeschild_link_fun: A function that takes a parent node targeted for deletion and returns the new links for their children. Required ifdeleteistrue. The
default function is new_child_link, which tries to be clever considering the parent and child links. See its help page for more information. If the link shouldn't be modified, use the link function instead.
Returns
- Nothing. The function modifies the
mtgin place.
Notes
If no geometry is found in the children nodes of type from, an informational message is logged.
PlantGeom._normalized_ref_meshes_dict — Method
write_opf(file, opf)Write an MTG with explicit geometry to disk as an OPF file.
PlantGeom.align_ref_meshes — Method
align_ref_meshes(meshes::Vector{<:RefMesh})Align all reference meshes along the X axis. Used for visualisation only.
PlantGeom.attributes_to_xml — Method
attributes_to_xml(node, xml_parent)Write an MTG node into an XML node.
PlantGeom.build_merged_mesh_with_map — Method
build_merged_mesh_with_map(mtg; filter_fun=nothing, symbol=nothing, scale=nothing, link=nothing)Traverse selected MTG nodes and merge their geometry meshes into a single mesh.
Returns a merged mesh and a face2node::Vector{Int} mapping each face index in the merged mesh to the originating MTG node id (MultiScaleTreeGraph.node_id(node)).
PlantGeom.build_merged_mesh_with_map_threaded — Method
build_merged_mesh_with_map_threaded(mtg; filter_fun=nothing, symbol=nothing, scale=nothing, link=nothing)Alias to build_merged_mesh_with_map. Threaded implementation removed.
PlantGeom.bump_scene_version! — Method
bump_scene_version!(mtg; by=1)Increment the scene version to invalidate any cached merged scene.
PlantGeom.circle_section_profile — Function
circle_section_profile(n_sides=8; radius=0.5, close_loop=true)Create a circular section profile in local section coordinates (XY plane, z=0).
This mirrors AMAPStudio's Mesh.makeCircle(n, radius) helper. When close_loop=true, the first point is repeated at the end.
PlantGeom.color_type — Method
color_type(color, opf)Return the type of the color, whether it is an attribute, a colorant, or a RefMeshColorant.
Arguments
color: The color to be checked.opf: The MTG to be plotted.
Returns
RefMeshColorantType: Ifcolorisnothing(the default) to color by reference mesh.DictRefMeshColorantType: If the color is a dictionary mapping reference meshes to colorants.DictVertexRefMeshColorantType: If the color is a dictionary mapping vertices to colorants.VectorColorantType: If the color is a vector of colorants, then we color each mesh by that vector.VectorSymbolType: If the color is a vector of symbols, then we color each mesh by that vector.AttributeColorantType: If the color is an attribute of the MTG, then we color by that attribute.T: If the color is a colorant, then we color everything by that color.
Examples
using MultiScaleTreeGraph, PlantGeom, Colors
file = joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","simple_plant.opf")
opf = read_opf(file)
# Colors:
color_type(:red, opf)
color_type(RGB(0.1,0.5,0.1), opf)
# Attributes:
color_type(:Length, opf)
# Default color:
color_type(nothing, opf)
# Dict of colors:
color_type(Dict(1=>RGB(0.1,0.5,0.1), 2=>RGB(0.5,0.1,0.1)), opf)PlantGeom.colorant_to_string — Method
colorant_to_string(x)Parse a geometry material for OPF writing.
PlantGeom.coordinates! — Method
coordinates!(mtg; angle = 45; force = false)Compute dummy 3d coordinates for the mtg nodes using an alterning phyllotaxy. Used when coordinates are missing. Coordinates are just node attributes with reserved names: :XX, :YY and :ZZ.
Returns
Nothing, mutates the mtg in-place (adds :XX, :YY and :ZZ to nodes).
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
coordinates!(mtg)
to_table(mtg, vars=[:XX, :YY, :ZZ])PlantGeom.default_amap_geometry_convention — Method
default_amap_geometry_convention(; angle_unit=:deg)Return a GeometryConvention close to AMAP/OpenAlea defaults:
- organ length aligned on the local
+Xaxis - insertion angles (
XInsertionAngle,YInsertionAngle,ZInsertionAngle) - local Euler angles (
XEuler,YEuler,ZEuler) - OPF-style translations (
XX,YY,ZZ) still supported
PlantGeom.diagram — Function
diagram(opf::MultiScaleTreeGraph.Node; kwargs...)
diagram!(opf::MultiScaleTreeGraph.Node; kwargs...)Make a diagram of the MTG tree using a Makie.jl backend.
This function is an extension to the package. It is only available if you imported a Makie backend (e.g. using GLMakie) prior to using PlantGeom.
The main attributes are:
- color: the color of the nodes
- colormap: the colormap used if the color uses an attribute. By default it uses viridis.
Must be a ColorScheme from ColorSchemes or a Symbol with its name.
Examples
using GLMakie, PlantGeom
file = joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","simple_plant.opf")
# file = joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","coffee.opf")
opf = read_opf(file)
diagram(opf)
# We can also color the 3d plot with several options:
# With one shared color:
diagram(opf, color = :red)
# Or colouring by opf attribute, *e.g.* using the nodes Z coordinates:
diagram(opf, color = :ZZ)PlantGeom.diagram! — Function
diagram(opf::MultiScaleTreeGraph.Node; kwargs...)
diagram!(opf::MultiScaleTreeGraph.Node; kwargs...)Make a diagram of the MTG tree using a Makie.jl backend.
This function is an extension to the package. It is only available if you imported a Makie backend (e.g. using GLMakie) prior to using PlantGeom.
The main attributes are:
- color: the color of the nodes
- colormap: the colormap used if the color uses an attribute. By default it uses viridis.
Must be a ColorScheme from ColorSchemes or a Symbol with its name.
Examples
using GLMakie, PlantGeom
file = joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","simple_plant.opf")
# file = joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","coffee.opf")
opf = read_opf(file)
diagram(opf)
# We can also color the 3d plot with several options:
# With one shared color:
diagram(opf, color = :red)
# Or colouring by opf attribute, *e.g.* using the nodes Z coordinates:
diagram(opf, color = :ZZ)PlantGeom.extend_pos — Method
Add a new point after (x1,y1) using same direction and length relative to it
PlantGeom.extrude_profile_mesh — Method
extrude_profile_mesh(section, path;
widths=nothing,
heights=nothing,
path_normals=nothing,
torsion=true,
close_section=nothing,
cap_ends=false)Build a GeometryBasics.Mesh by sweeping a profile section along a 3D path.
This is an AMAP-style extrusion primitive (similar in spirit to AMAPStudio's MeshBuilder.ExtrudeData + ExtrudedMesh):
section: profile points in local section coordinates.path: centerline points.widths/heights: per-path scaling of the section local axes.path_normals: optional per-path local normal vectors.torsion: iffalse, uses a fixed-section normal plane (reprojected along path).close_section: iftrue, connects last/first section points; ifnothing, auto-detect.cap_ends: iftrueand section is closed, adds start/end caps.
The returned mesh is expressed in local coordinates, ready to be wrapped in RefMesh.
PlantGeom.extrude_profile_refmesh — Method
extrude_profile_refmesh(name, section, path;
material=RGB(220 / 255, 220 / 255, 220 / 255),
cache=nothing,
widths=nothing,
heights=nothing,
path_normals=nothing,
torsion=true,
close_section=nothing,
cap_ends=false)Create a RefMesh directly from extrude_profile_mesh.
Extrusion options are forwarded to extrude_profile_mesh.
PlantGeom.extrude_tube_mesh — Method
extrude_tube_mesh(path;
n_sides=8,
radius=0.5,
radii=nothing,
widths=nothing,
heights=nothing,
path_normals=nothing,
torsion=true,
cap_ends=false)Convenience wrapper to extrude a circular section along a path.
radius: base circular section radius.radii: per-path isotropic scaling (applied to both width and height).widths/heights: optional anisotropic per-path scaling.
widths/heights take precedence over radii when explicitly provided.
PlantGeom.extrusion_make_curve — Method
extrusion_make_curve(z_keys, r_keys, n)AMAP-style radial curve helper (similar to Mesh.makeCurve), used by lathe.
This interpolation preserves local extrema in r_keys by forcing zero slope at intermediate local minima/maxima, then sampling with cubic Hermite interpolation. Returns (z_samples, r_samples) with n + 1 values each.
PlantGeom.extrusion_make_interpolation — Method
extrusion_make_interpolation(n, key_values)AMAP-style scalar interpolation helper (similar to Mesh.makeInterpolation).
Returns n + 1 scalar values sampled linearly between key values.
PlantGeom.extrusion_make_path — Method
extrusion_make_path(n, key_points; key_tangents=nothing)AMAP-style Hermite path interpolation helper (similar to Mesh.makePath).
Returns n + 1 sampled 3D points passing through the key points. If key_tangents is omitted, tangents are estimated from neighboring keys.
PlantGeom.extrusion_make_spline — Method
extrusion_make_spline(n, key_points)AMAP-style spline helper (similar to Mesh.makeSpline) using Catmull-Rom interpolation.
Returns n + 1 sampled 3D points passing near the key points.
PlantGeom.geometry_from_attributes — Method
geometry_from_attributes(node, ref_mesh; convention=default_geometry_convention(), dUp=1.0, dDwn=1.0, warn_missing=false)Create a Geometry from node attributes and a reference mesh.
PlantGeom.geometry_to_mesh — Method
geometry_to_mesh(geom)Materialize a geometry object into a concrete mesh.
This is an internal extension point used by scene merging and rendering. The default PlantGeom method supports Geometry. Additional geometry sources can provide their own method without changing the rendering API.
PlantGeom.get_attr_type — Method
Get the attributes types in Julia DataType.
PlantGeom.get_cached_scene — Method
get_cached_scene(mtg, key) -> Union{Nothing,NamedTuple}Retrieve the single cached merged scene if it matches key. Returns a NamedTuple with (hash, mesh, face2node) or nothing.
PlantGeom.get_color — Method
get_color(var <: AbstractArray, range_var, colormap=colorschemes[:viridis])
get_color(var, range_var, colormap=colorschemes[:viridis])Map value(s) to colors from a colormap based on a range of values
Arguments
var: value(s) to map to colorsrange_var: range of values to map to colorscolormap: colormap to use
Returns
color: color(s) corresponding tovar
Examples
using Colors
get_color(1, 1:2, colormap = colorschemes[:viridis]) # returns RGB{N0f8}(0.267004,0.00487433,0.329415)
get_color(1:2, 1:10, colormap = colorschemes[:viridis]) # returns RGB{N0f8}(0.267004,0.00487433,0.329415)
get_color(1:2, 1:10, 1, colormap = colorschemes[:viridis]) # returns RGB{N0f8}(0.267004,0.00487433,0.329415)PlantGeom.get_color_range — Method
get_color_range(colorrange, opf, colorant)Get the color range from the colorrange argument or from the MTG attribute.
Arguments
colorrange: the color range specified by the user, can be an Observable or a tuple of two values.opf: the MTG object.colorant: the color attribute to use for the range.
Returns
colorrange: the color range as a tuple of two values.
PlantGeom.get_colormap — Method
get_colormap(colormap)Get the colormap as a ColorScheme if it is a named color or ColorScheme
PlantGeom.get_mtg_color — Method
get_mtg_color(color, opf)Return the color to be used for the plot.
Arguments
color: The color to be checked.opf: The MTG to be plotted.
Returns
The color to be used for the plot.
Examples
using MultiScaleTreeGraph, PlantGeom, Colors
file = joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","simple_plant.opf")
opf = read_opf(file)
get_mtg_color(:red, opf)
get_mtg_color(RGB(0.1,0.5,0.1), opf)
get_mtg_color(:Length, opf)
get_mtg_color(:slategray3, opf)
get_mtg_color(Dict(1=>RGB(0.1,0.5,0.1), 2=>RGB(0.1,0.1,0.5)), opf)
get_mtg_color(Dict(1 => :burlywood4, 2 => :springgreen4), opf)PlantGeom.get_ref_mesh_name — Method
get_ref_mesh_name(node)Get the name of the reference mesh used for the current node.
PlantGeom.get_ref_meshes — Method
get_ref_meshes(mtg)Get all reference meshes from an mtg, usually from an OPF.
PlantGeom.get_ref_meshes_color — Method
get_ref_meshes_color(meshes)Get the reference meshes colors (only the diffuse part for now).
Arguments
meshes::Dict{Int, RefMesh}: Dictionary of reference meshes as returned byparse_ref_meshesmeshes::AbstractVector{<:RefMesh}: Vector/list of reference meshes (legacy and plotting workflows)
Returns
Dict{String, Colorant}: Dictionary mapping mesh names to their diffuse colors
Notes
- Only the diffuse component of the material is used for the color
- Dictionary input preserves OPF shape-ID keyed workflows
PlantGeom.has_geometry — Method
has_geometry(node)Return true if the node has geometry, false otherwise.
PlantGeom.lathe_gen_mesh — Method
lathe_gen_mesh(n_sides, z_coords, radii;
axis=:x, cap_ends=false)AMAP-style lathe generator (similar to latheGen): revolve sampled radii around the main axis.
PlantGeom.lathe_gen_refmesh — Method
lathe_gen_refmesh(name, n_sides, z_coords, radii;
material=RGB(220 / 255, 220 / 255, 220 / 255),
cache=nothing,
axis=:x,
cap_ends=false)Create a RefMesh from lathe_gen_mesh.
PlantGeom.lathe_mesh — Method
lathe_mesh(n_sides, n, z_keys, r_keys;
method=:curve,
axis=:x,
cap_ends=false)AMAP-style lathe with key profiles (similar to lathe):
method=:curvematches AMAPmakeCurvebehavior (local extrema preserving).method=:splineuses Catmull-Rom interpolation.method=:pathuses Hermite interpolation.
PlantGeom.lathe_refmesh — Method
lathe_refmesh(name, n_sides, n, z_keys, r_keys;
material=RGB(220 / 255, 220 / 255, 220 / 255),
cache=nothing,
method=:curve,
axis=:x,
cap_ends=false)Create a RefMesh from lathe_mesh.
PlantGeom.leaflet_midrib_profile — Method
leaflet_midrib_profile(; lamina_angle_deg=40.0, scale=0.5)Return a 3-point open section profile commonly used to mimic a leaflet with a central midrib (AMAP-style V section).
The profile lies in local section coordinates and is typically swept along the organ length axis with extrude_profile_mesh.
PlantGeom.map_coord — Method
map_coord(f, mesh, coord)Apply function f over the mesh coordinates coord. Values for coord can be 1 for x, 2 for y and 3 for z.
PlantGeom.materialBDD_to_material — Method
Parse a material in opf format to a Phong material.
PlantGeom.material_to_opf_string — Method
material_to_opf_string(material::Phong)
material_to_opf_string(material::Colorant)Format a material into a Dict for OPF writting.
PlantGeom.merge_simple_meshes — Method
merge_simple_meshes(meshes) -> meshMerge a collection of meshes into a single mesh in one pass by concatenating vertices and reindexing faces with running offsets.
PlantGeom.meshBDD_to_meshes — Method
meshBDD_to_meshes(x)PlantGeom.mtg_coordinates_df — Function
mtg_coordinates_df(mtg, attr; force = false)
mtg_coordinates_df!(mtg, attr; force = false)Extract the coordinates of the nodes of the mtg and the coordinates of their parents (:XXfrom, :YYfrom, :ZZ_from) and output a DataFrame. Optionally you can also provide an attribute to add to the output DataFrame too by passing its name as a symbol to attr.
The coordinates are computed using coordinates! if missing, or if force = true.
PlantGeom.mtg_to_opf_link — Method
mtg_to_opf_link(link)PlantGeom.mtg_topology_to_xml! — Function
mtg_topology_to_xml!(node, xml_parent)Write the MTG topology, attributes and geometry into XML format.
PlantGeom.nelements — Method
nelements(meshes::RefMesh)Return the number of triangular elements of a reference mesh.
PlantGeom.normals_vertex — Method
normals_vertex(mesh)Compute per vertex normals and return them as GeometryBasics.Vec{3,Float64}.
PlantGeom.nvertices — Method
nvertices(meshes::RefMesh)Return the number of vertices of a reference mesh.
PlantGeom.parse_geometry — Method
Parse the geometry element of the OPF.
Note
The transformation matrix is 3*4. elem = elem.content
PlantGeom.parse_materialBDD! — Method
parse_materialBDD!(node; file=nothing)Parse the materialBDD using parse_opf_elements!.
When the section is present but empty, a neutral default material is inserted so OPF files without explicit materials remain readable.
PlantGeom.parse_meshBDD! — Method
parse_meshBDD!(node; file="")Parse the meshBDD using parse_opf_array.
Supports both flat <faces> arrays and nested <face> elements. Polygon faces with more than three vertices are triangulated with a fan strategy.
PlantGeom.parse_opf_array — Function
Parse an array of values from the OPF into a Julia array (Arrays in OPFs are not following XML recommendations)
PlantGeom.parse_opf_attributeBDD! — Method
Parse the opf attributes as a Dict.
PlantGeom.parse_opf_elements! — Method
Generic parser for OPF elements.
Arguments
opf::OrderedDict: the opf Dict (using [XMLDict.xml_dict])elem_types::Array: the target types of the element (e.g. "[String, Int64]")
Details
elem_types should be of the same length as the number of elements found in each item of the subchild. elem_types = [Float64, Float64, Float64, Float64, Float64, Float64]
PlantGeom.parse_opf_topology! — Function
parse_opf_topology!(node, mtg, features, attr_type, mtg_type, ref_meshes, ...)Parser of the OPF topology.
Arguments
node::ElementNode: the XML node to parse.mtg::Union{Nothing,Node}: the parent MTG node.features::Dict: the features of the OPF.attr_type::DataType: the type of the attributes to use.mtg_type::DataType: the type of the MTG to use.ref_meshes::Dict: the reference meshes.read_id::Bool: whether to read the ID from the OPF or recompute it on the fly.max_id::RefValue{Int64}=Ref(1): the ID of the first node, ifread_id==false.attributeBDD::Dict{String,String}=Dict{String,String}(): the attributeBDD for dynamic attribute type discovery.attribute_types::Dict{String,DataType}=Dict{String,DataType}(): explicit user type mapping.dynamic_attributes::Set{String}=Set{String}(): names of attributes inferred dynamically.
Note
The transformation matrices in geometry are 3*4.
PlantGeom.parse_ref_meshes — Method
parse_ref_meshes(opf_attr)Parse the reference meshes from OPF attributes into a dictionary.
Arguments
opf_attr::Dict: Dictionary containing OPF attributes including:meshBDD,:materialBDD, and:shapeBDD
Returns
Dict{Int, RefMesh}: A dictionary mapping shape IDs to RefMesh objects
Notes
- The returned dictionary uses the actual shape IDs from the OPF file as keys
- This differs from the previous implementation which returned an array with 1-based indexing
- Shape IDs, mesh indices, and material indices are used as-is from the OPF file (0-based)
PlantGeom.plantviz — Function
plantviz(mtg::MultiScaleTreeGraph.Node; kwargs...)
plantviz!(mtg::MultiScaleTreeGraph.Node; kwargs...)Vizualise the 3D geometry of an MTG (usually read from an OPF). This function search for the :geometry attribute in each node of the MTG, and build the vizualisation using the reference meshes and the associated transformation matrix.
Arguments
mtg: The MTG to be vizualised.kwargs: Additional arguments to be passed toplantviz!, wich includes:color: The color to be used for the plot. Can be a colorant, an attribute of the MTG (given as a Symbol), or a dictionary of colors for each reference mesh.colormap: The colorscheme to be used for the plot. Can be a Symbol or a ColorScheme.segmentcolor: The color to be used for the facets. Should be a colorant or a symbol of color.showsegments: A boolean indicating whether the facets should be shown or not.color_missing=RGBA(0, 0, 0, 0.3): The color to be used for missing values. Should be a colorant or a symbol of color.index: An integer giving the index of the attribute value to be vizualised. This is useful when the attribute is a vector of values for e.g. each timestep.color_cache_name: The name of the color cache. Should be a string (default to a random string).filter_fun: A function to filter the nodes to be plotted. Should be a function taking a node as argument and returning a boolean.symbol: Plot only nodes with this symbol. PreferSymbol(or vector/tuple of symbols).scale: Plot only nodes with this scale. Should be an Int or a vector of.link: Plot only nodes with this link. PreferSymbol(or vector/tuple of symbols).
Examples
using MultiScaleTreeGraph, PlantGeom, GLMakie
file = joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","simple_plant.opf")
# file = joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","coffee.opf")
mtg = read_opf(file)
plantviz(mtg)
# Then plot it again like before, and it will be faster:
plantviz(mtg)
# We can color the 3d plot with several options:
# With one shared color:
plantviz(mtg, color = :red)
# One color per reference mesh:
plantviz(mtg, color = Dict(1 => :burlywood4, 2 => :springgreen4, 3 => :burlywood4))
# Or just changing the color of some:
plantviz(mtg, color = Dict(1 => :burlywood4))
# Or coloring by mtg attribute, e.g. using the mesh max Z coordinates:
transform!(mtg, zmax => :z_max, ignore_nothing = true)
plantviz(mtg, color = :z_max)
# One color for each vertex of the refmesh 1:
vertex_color = get_color(1:nvertices(get_ref_meshes(mtg))[1], [1,nvertices(get_ref_meshes(mtg))[1]])
plantviz(mtg, color = Dict(1 => vertex_color))
# Or even coloring by the value of the Z coordinates of each vertex:
transform!(
mtg,
(x -> [v[3] for v in GeometryBasics.coordinates(refmesh_to_mesh(x))]) => :z_vertex,
filter_fun=node -> hasproperty(node, :geometry)
)
plantviz(mtg, color = :z, showsegments = true)
f,a,p = plantviz(mtg, color = :z, showsegments = true)
p[:color] = :Lengthplantviz!(ref_meshes; kwargs...)Plot all reference meshes in a single 3d plot using Makie.
Examples
using PlantGeom, GLMakie
file = joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","simple_plant.opf")
mtg = read_opf(file)
meshes = get_ref_meshes(mtg)
plantviz(meshes)
# With one shared color:
plantviz(meshes, color = :green)
# One color per reference mesh:
plantviz(meshes, color = Dict(1 => :burlywood4, 2 => :springgreen4, 3 => :burlywood4))
# Or just changing the color of some:
plantviz(meshes, color = Dict(1 => :burlywood4, 3 => :burlywood4))
# One color for each vertex of the refmesh 0:
plantviz(meshes, color = Dict(2 => 1:nvertices(meshes)[2]))PlantGeom.plantviz! — Function
viplantviz!(mtg; [options])Visualize the 3D meshes of an MTG using GeometryBasics and Makie. This function adds the plot to an existing scene with options forwarded to plantviz.
PlantGeom.read_opf — Method
read_opf(file; attr_type = Dict, mtg_type = MutableNodeMTG, attribute_types = Dict())Read an OPF file, and returns an MTG.
Arguments
file::String: The path to the opf file.attr_type::DataType = Dict: kept for backward compatibility and ignored for MultiScaleTreeGraph >= v0.15 (typed columnar attributes backend is always used).mtg_type = MutableNodeMTG: the type used to hold the mtg encoding for each node (i.e.
link, symbol, index, scale). See details section below.
read_id::Bool = true: whether to read the ID from the OPF or recompute it on the fly.max_id::RefValue{Int64}=Ref(1): the ID of the first node, ifread_id==false.attribute_types::Dict = Dict(): optional explicit mapping from attribute name (StringorSymbol) to Julia type (Int*,Float*,Bool,String). When provided, it overridesattributeBDD.
Each parsed topology node stores its original OPF id in :source_topology_id.
Details
attr_type is ignored with MultiScaleTreeGraph >= v0.15 where the typed columnar backend is always used.
The MultiScaleTreeGraph package provides two types for mtg_type, one immutable (NodeMTG), and one mutable (MutableNodeMTG). If you're planning on modifying the mtg encoding of some of your nodes, you should use MutableNodeMTG, and if you don't want to modify anything, use NodeMTG instead as it should be faster.
Note
See the documentation of the MTG format from the MTG package documentation for further details, e.g. The MTG concept.
Returns
The MTG root node. OPF reference meshes are attached on the root as opf[:ref_meshes]::Dict{Int,RefMesh}, keyed by OPF shape IDs (shapeIndex, typically 0-based).
Examples
using PlantGeom
file = joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","simple_plant.opf")
# file = joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","coffee.opf")
opf = read_opf(file)PlantGeom.read_ops — Method
read_ops(file; attr_type=Dict{String,Any}, mtg_type=MutableNodeMTG, attribute_types=Dict(), kwargs...)Reads an OPS file and returns the content as a MultiScaleTreeGraph. Per-object OPS transforms (rotation, scale, inclinationAzimut/inclinationAngle, and pos) are applied to geometry during loading. Geometry nodes preserve their original object-local topology ids in :source_topology_id (from OPF/GWA files), while MTG indices remain unique at scene scope.
Additional keyword arguments are forwarded to read_ops_file, e.g. relaxed=true and assume_scale_column=false for legacy OPS rows where the scale column is missing.
attr_type is kept for backward compatibility and ignored with MultiScaleTreeGraph >= v0.15 (columnar attributes backend).
attribute_types is forwarded to read_opf and can be used to override OPF attribute types by name (CSV-like typing override).
PlantGeom.read_ops_file — Method
read_ops_file(file; relaxed=false, assume_scale_column=true, opf_scale=1.0, gwa_scale=1.0, require_functional_group=false, default_functional_group="")Read the content of an .ops file and return a tuple with the scene dimensions and the object table.
Arguments
file::String: Path of the.opsfile to read.relaxed::Bool=false: Iftrue, parse rows using whitespace separators and accept extra trailing columns in object rows.assume_scale_column::Bool=true: Iffalse, interpret object rows as legacyx y z inclinationAzimut inclinationAngle rotation [ignored...](missing scale) and inject scale values usingopf_scale/gwa_scale.opf_scale::Float64=1.0: Default object scale applied to.opfrows whenassume_scale_column=false.gwa_scale::Float64=1.0: Default object scale applied to.gwarows whenassume_scale_column=false.require_functional_group::Bool=false: Iftrue, throw when parsing an object row before any#[Archimed] ...section header.default_functional_group::AbstractString="": Value assigned tofunctional_groupwhen no#[Archimed] ...header is active.
Returns
The scene dimensions and the object table as a tuple. The scene dimensions are a tuple of two GeometryBasics.Point{3,Float64} with the origin point and opposite point of the scene. The object table is an array of NamedTuple with the following fields:
sceneID::Int: Scene ID.plantID::Int: Plant ID.filePath::String: Path to the.opfor.gwafile.pos::GeometryBasics.Point{3,Float64}: Position of the object.scale::Float64: Scale of the object.inclinationAzimut::Float64: Inclination azimut of the object.inclinationAngle::Float64: Inclination angle of the object.rotation::Float64: Rotation of the object.functional_group::String: Functional group of the object.
PlantGeom.reconstruct_geometry_from_attributes! — Method
reconstruct_geometry_from_attributes!(mtg, ref_meshes;
convention=default_amap_geometry_convention(),
conventions=Dict(),
offset_aliases=[:Offset, :offset],
border_offset_aliases=[:BorderInsertionOffset, :border_insertion_offset, :BorderOffset, :border_offset],
insertion_mode_aliases=[:InsertionMode, :insertion_mode],
phyllotaxy_aliases=[:Phyllotaxy, :phyllotaxy, :PHYLLOTAXY],
verticil_mode=:rotation360,
amap_options=default_amap_reconstruction_options(),
dUp=1.0,
dDwn=1.0,
warn_missing=false,
root_align=true,
)Reconstruct node geometries from attribute conventions and MTG topology.
When no explicit translation attributes are found (XX/YY/ZZ by default), placement follows a topological convention close to AMAP:
:<: attach to predecessor top:+: attach to bearer atOffset(or bearer length if missing):+: default insertion mode isBORDER, adding a lateral offset ofBorderInsertionOffset(or bearer top width / 2):/: attach to parent base
If endpoint attributes (EndX/EndY/EndZ aliases) are present, they override angle-derived orientation and Length for that node: base position comes from translation/topology, and orientation+length are inferred from (base -> end).
PlantGeom.refmesh_to_mesh — Function
refmesh_to_mesh(node)Compute a node mesh based on the reference mesh, the transformation matrix and the tapering.
Examples
using PlantGeom
file = joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","simple_plant.opf")
opf = read_opf(file)
node = opf[1][1][1]
new_mesh = refmesh_to_mesh(node)
using GLMakie
plantviz(new_mesh)PlantGeom.rotate_point — Method
Rotate a point (x1,y1) around (x0, y0) with angle.
PlantGeom.scene_cache_key — Method
scene_cache_key(mtg; symbol=nothing, scale=nothing, link=nothing, filter_fun=nothing) -> UIntCompute a stable cache key for the current scene rendering request.
PlantGeom.scene_version — Method
scene_version(mtg) -> IntReturn the scene version counter stored on the MTG root (default 0).
PlantGeom.set_cached_scene! — Method
set_cached_scene!(mtg, key; mesh, face2node=nothing)Store a single merged scene cache with associated key hash. Only mesh and face2node are cached.
PlantGeom.set_geometry_from_attributes! — Method
set_geometry_from_attributes!(node, ref_mesh; convention=default_geometry_convention(), dUp=1.0, dDwn=1.0, warn_missing=false)Compute and assign node[:geometry] from attribute conventions.
PlantGeom.taper — Method
Returns a tapered mesh using dDwn and dUp based on the geometry of an input mesh. Tapering a mesh transforms it into a tapered version (i.e. pointy) or enlarged object, e.g. make a cone from a cylinder.
PlantGeom.transform_mesh! — Method
transform_mesh!(node::Node, transformation)Add a new transformation to the node geometry transformation field. The transformation is composed with the previous transformation if any.
transformation must be a CoordinateTransformations.Transformation.
PlantGeom.transformation_from_attributes — Method
transformation_from_attributes(node; convention=default_geometry_convention(), warn_missing=false)Build a CoordinateTransformations.Transformation from node attributes using a GeometryConvention.
PlantGeom.write_gwa — Method
write_gwa(file, mtg)Write an MTG object to disk as a GWA mesh file.
PlantGeom.write_ops — Method
write_ops(file, scene_dimensions, object_table)Write only the scene table (.ops) from explicit scene dimensions and object rows. Alias to write_ops_file.
PlantGeom.write_ops — Method
write_ops(file, scene; write_objects=true, objects_subdir="objects", preserve_file_paths=false)Write a scene MTG to an .ops file. By default this writes one object file per scene child (.opf / .gwa) and emits an OPS row pointing to each object.
write_objects=true: also write object files next to the.opsfile.objects_subdir="objects": target subdirectory for generated object files whenpreserve_file_paths=false.preserve_file_paths=false: whentrue, reuse each childfilePath(sanitized to remain relative) for emitted object paths.
PlantGeom.write_ops_file — Method
write_ops_file(file, scene_dimensions, object_table)Write only the scene table (.ops) from scene dimensions and object rows. This does not write referenced .opf / .gwa object files.
PlantGeom.xmax — Function
xmax(x)
ymax(x)
zmax(x)Get the maximum x, y or z coordinates of a mesh or a Node.
PlantGeom.xmin — Function
xmin(x)
ymin(x)
zmin(x)Get the minimum x, y or z coordinates of a mesh or a Node.
PlantGeom.ymax — Function
xmax(x)
ymax(x)
zmax(x)Get the maximum x, y or z coordinates of a mesh or a Node.
PlantGeom.ymin — Function
xmin(x)
ymin(x)
zmin(x)Get the minimum x, y or z coordinates of a mesh or a Node.
PlantGeom.zmax — Function
xmax(x)
ymax(x)
zmax(x)Get the maximum x, y or z coordinates of a mesh or a Node.
PlantGeom.zmin — Function
xmin(x)
ymin(x)
zmin(x)Get the minimum x, y or z coordinates of a mesh or a Node.
RecipesBase.plot — Function
plot(opf::MultiScaleTreeGraph.Node; kwargs...)
plot!(opf::MultiScaleTreeGraph.Node; kwargs...)Make a diagram of the MTG tree, paired with a Plots.jl backend.
See also diagram for the same plot with a Makie.jl backend.
Attributes
mode = "2d": The mode for plotting, either "2d" or "3d"node_color = :black: the node color, can be a color or any MTG attributeedge_color = node_color: same asnode_color, but for the edgescolormap = :viridis: the colormap used for coloringcolor_missing = RGBA(0, 0, 0, 0.3): The color used for missing values
Examples
# import Pkg; Pkg.add("PlotlyJS")
using Plots, PlantGeom
plotlyjs()
file = joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","simple_plant.opf")
# file = joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","coffee.opf")
opf = read_opf(file)
plot(opf, node_color = :Length)RecipesBase.plot! — Function
plot(opf::MultiScaleTreeGraph.Node; kwargs...)
plot!(opf::MultiScaleTreeGraph.Node; kwargs...)Make a diagram of the MTG tree, paired with a Plots.jl backend.
See also diagram for the same plot with a Makie.jl backend.
Attributes
mode = "2d": The mode for plotting, either "2d" or "3d"node_color = :black: the node color, can be a color or any MTG attributeedge_color = node_color: same asnode_color, but for the edgescolormap = :viridis: the colormap used for coloringcolor_missing = RGBA(0, 0, 0, 0.3): The color used for missing values
Examples
# import Pkg; Pkg.add("PlotlyJS")
using Plots, PlantGeom
plotlyjs()
file = joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","simple_plant.opf")
# file = joinpath(dirname(dirname(pathof(PlantGeom))),"test","files","coffee.opf")
opf = read_opf(file)
plot(opf, node_color = :Length)