diff --git a/examples/example_texture.py b/examples/example_texture.py index 4d07700..d40fce1 100755 --- a/examples/example_texture.py +++ b/examples/example_texture.py @@ -16,12 +16,12 @@ ############################################################################### # Importation of slam modules -import os +# import os from pathlib import Path import numpy as np from slam import texture from slam import io as sio -from slam import plot as plt +from slam import plot as proj ############################################################################### # @@ -48,38 +48,89 @@ sio.write_texture(tex2, "test.gii") ############# -print('extremum texture') -mesh = sio.load_mesh("../examples/data/example_mesh.gii") -print('maximum') +print("extremum texture") +print("maximum") print(np.count_nonzero(tex.extremum(mesh) == 1)) -print('minimum') +print("minimum") print(np.count_nonzero(tex.extremum(mesh) == -1)) ############################################################################### # plot -# dict for proj +# reorient the mesh +mesh.apply_transform(mesh.principal_inertia_transform) +theta = np.pi / 2 +rot_x = np.array( + [[1, 0, 0], + [0, np.cos(theta), -np.sin(theta)], + [0, np.sin(theta), np.cos(theta)]] +) +vertices_translate = np.dot(rot_x, mesh.vertices.T).T + +# parameters for the projection +# Complete example with all modifiable parameters NAME_TEX = "sulc" TITLE = "test" -EXT = "png" +EXT = "html" PATH = Path("./test") SAVE_DIR = PATH / f"{TITLE}.{EXT}" +data = tex.darray[0] + mesh_data = { - "vertices": mesh.vertices, + "vertices": vertices_translate, "faces": mesh.faces, - "center": mesh.center_mass, - "title": TITLE + "title": TITLE, # figure title, default is None +} + +intensity_data = { + "values": data, + "mode": "vertex", # default is "cell" + "cmin": 0, # default is automatic value + "cmax": 1, } -intensity_data = {"values": tex.darray[0], "mode": "vertex"} -display_settings = {"colorscale": "Turbo", "colorbar_label": NAME_TEX} +display_settings = { + "colorscale": "RdBu", # color scale, default is Turbo + "colorbar_label": NAME_TEX, # colorbar label, default is None + "template": "plotly_dark", # default is blank theme without axes + "tickvals": [ + 0, + 0.25, + 0.5, + 0.75, + 1, + ], # default is None, sets exact tick positions on the colorbar + "ticktext": [ + "0%", + "25%", + "50%", + "75%", + "100%", + ], # default is None, customizes tick labels on the colorbar +} -fig = plt.mes3d_projection( +fig = proj.mesh_projection( mesh_data, intensity_data, display_settings, + caption=True, # snapshot, default is None ) -#os.makedirs(PATH, exist_ok=True) -#fig.write_image(SAVE_DIR, width=1600, height=900) +# add an additional trace +# example: display vertex index on hover and customize vertex color and size +# over the mesh +hover_text = [f"vertex {i}" for i in range(len(vertices_translate))] +trace_hover = proj.create_hover_trace( + vertices_translate, + text=hover_text, + marker={"size": 4, "color": "blue"}, +) + +fig.add_trace(trace_hover, row=1, col=1) +fig.add_trace(trace_hover, row=1, col=2) + +# save the figure as an HTML file +# os.makedirs(PATH, exist_ok=True) +# fig.write_html(SAVE_DIR) +# fig.write_image(SAVE_DIR, width=1600, height=900) diff --git a/examples/test.gii b/examples/test.gii deleted file mode 100644 index 841a02e..0000000 --- a/examples/test.gii +++ /dev/null @@ -1,9 +0,0 @@ - - -NIFTI_XFORM_UNKNOWNNIFTI_XFORM_UNKNOWN 1.000000 0.000000 0.000000 0.000000 - 0.000000 1.000000 0.000000 0.000000 - 0.000000 0.000000 1.000000 0.000000 - 0.000000 0.000000 0.000000 1.000000eJxjYEAAAAAMAAE=NIFTI_XFORM_UNKNOWNNIFTI_XFORM_UNKNOWN 1.000000 0.000000 0.000000 0.000000 - 0.000000 1.000000 0.000000 0.000000 - 0.000000 0.000000 1.000000 0.000000 - 0.000000 0.000000 0.000000 1.000000eJxjYEAAAAAMAAE= \ No newline at end of file diff --git a/slam/plot.py b/slam/plot.py index 8c28503..7be4c13 100755 --- a/slam/plot.py +++ b/slam/plot.py @@ -1,77 +1,81 @@ """ -plots.py. +plots.py -Centralisation des fonctions pour plots et figures. +Centralized functions for plotting and figures. -Auteur : Zoë LAFFITTE -Date : 2026 +Author: Zoë LAFFITTE +Date: 2026 """ import plotly.graph_objects as go +from plotly.subplots import make_subplots -def create_hover_trace(points, text, mode, **kwargs): +def create_hover_trace(points, text, mode="markers", **kwargs): """ - Crée une trace 3D pour un graphique Plotly. + Creates a 3D trace for a Plotly figure with customizable options. Parameters ---------- points : array-like - Coordonnées des points. - - labels : array-like - Labels associés aux points. + Coordinates of the points (N, 3). text : list - Les informations de survol. + Hover information for each point. - mode : str - Mode d'affichage des points (e.g., "markers", "lines"). + mode : str, optional + Display mode for the points (e.g., "markers", "lines"), + default is "markers". **kwargs : dict - Arguments supplémentaires pour personnaliser la trace. + Additional arguments to customize the trace (e.g., marker, hoverlabel). Returns ------- plotly.graph_objects.Scatter3d - La trace 3D avec survol. + The 3D trace with hover functionality. """ - # Création d'une trace 3D avec survol pour chaque point return go.Scatter3d( x=points[:, 0], y=points[:, 1], z=points[:, 2], mode=mode, - marker={"size": 1, "color": "rgba(0,0,0,0)"}, text=text, hoverinfo="text", showlegend=False, + **kwargs ) -def mes3d_projection(mesh_data, intensity_data=None, display_settings=None): +def mesh_projection( + mesh_data, intensity_data=None, display_settings=None, caption=None +): """ - Crée une projection 3D d'un maillage, avec ou sans intensités. + Creates a 3D projection of a mesh, with or without intensity data. Parameters ---------- mesh_data : dict - Contient les coordonnées des sommets et les indices des faces. + Contains the vertex coordinates and face indices of the mesh. + intensity_data : dict, optional - Contient les valeurs d'intensité et le mode d'affichage. - Si None, le maillage est tracé sans texture. + Contains intensity values and display mode. + If None, the mesh is plotted without a texture. + display_settings : dict, optional - Paramètres d'affichage (e.g., colorscale, labels). + Display parameters (e.g., colorscale, labels). Returns ------- fig : plotly.graph_objects.Figure - L'objet figure Plotly. + The resulting Plotly figure object. """ + vertices = mesh_data["vertices"] faces = mesh_data["faces"] title = mesh_data.get("title", "") + template = display_settings.get("template", None) lighting = { "ambient": 0.7, @@ -93,54 +97,92 @@ def mes3d_projection(mesh_data, intensity_data=None, display_settings=None): "lighting": lighting, } - if intensity_data is not None: - mesh_kwargs.update({ - "intensity": intensity_data["values"], - "intensitymode": intensity_data.get("mode", "cell"), - "colorscale": display_settings.get("colorscale", "Turbo"), - "cmin": intensity_data.get("cmin", None), - "cmax": intensity_data.get("cmax", None), - "colorbar": { - "title": display_settings.get("colorbar_label", ""), - "len": 0.85, - "thickness": 25, - "tickfont": {"size": 16}, - }, - "flatshading": True, - "lighting": { - "ambient": 1, - "diffuse": 0, - "specular": 0, - "roughness": 1, - "fresnel": 0, - }, - "colorbar_tickvals": display_settings.get("tickvals", None), - "colorbar_ticktext": display_settings.get("ticktext", None), - }) - camera = dict( - eye=dict(x=2, y=0, z=0), # Camera position from lateral side - center=dict(x=0, y=0, z=0), # Looking at center - up=dict(x=0, y=0, z=1) # Up vector points in positive z direction - ) - fig = go.Figure(data=[go.Mesh3d(**mesh_kwargs)]) - - fig.update_layout( - title=title, - title_x=0.2, - template="seaborn", - scene=dict( - aspectmode="data", - xaxis=dict(visible=False), - yaxis=dict(visible=False), - zaxis=dict(visible=False), - camera=camera - ), - legend={ + camera = { + # Camera position from lateral side + "eye": {"x": 2.5, "y": 0, "z": 0.0}, + # Looking at center + "center": {"x": 0, "y": 0, "z": 0}, + # Up vector points in positive z direction + "up": {"x": 0, "y": 0, "z": 1}, + } + + aff_dict = { + "title": title, + "title_x": 0.2, + "height": 900, + "width": 1200, + "template": template, + "legend": { "x": 0, "y": 1, "xanchor": "left", "yanchor": "top", }, - ) + "scene": {"camera": camera}, + } + + if intensity_data is not None: + mesh_kwargs.update( + { + "intensity": intensity_data["values"], + "intensitymode": intensity_data.get("mode", "cell"), + "colorscale": display_settings.get("colorscale", "Turbo"), + "cmin": intensity_data.get("cmin", None), + "cmax": intensity_data.get("cmax", None), + "colorbar": { + "title": display_settings.get("colorbar_label", ""), + "len": 0.85, + "thickness": 25, + "tickfont": {"size": 16}, + }, + "flatshading": True, + "lighting": { + "ambient": 1, + "diffuse": 0, + "specular": 0, + "roughness": 1, + "fresnel": 0, + }, + "colorbar_tickvals": display_settings.get("tickvals", None), + "colorbar_ticktext": display_settings.get("ticktext", None), + } + ) + + if caption: + fig = make_subplots( + rows=1, + cols=2, + specs=[[{"type": "scene"}, {"type": "scene"}]], + horizontal_spacing=0.03, + ) + for col in [1, 2]: + fig.add_trace(go.Mesh3d(**mesh_kwargs), row=1, col=col) + + aff_dict["scene2"] = { + "camera": { + # Camera position from lateral side + "eye": {"x": -2.5, "y": 0, "z": 0.0}, + # Looking at center + "center": {"x": 0, "y": 0, "z": 0}, + # Up vector points in positive z direction + "up": {"x": 0, "y": 0, "z": 1}, + } + } + + else: + fig = go.Figure(data=[go.Mesh3d(**mesh_kwargs)]) + + if template is None: + aff_dict["template"] = "simple_white" + aff_dict["scene"].update( + { + "aspectmode": "data", + "xaxis": {"visible": False}, + "yaxis": {"visible": False}, + "zaxis": {"visible": False}, + } + ) + + fig.update_layout(**aff_dict) return fig