I’m using Plotly Mesh3D
to display an obj file. I’m also using Dash for callbacks using ClickData
. However, on clicking on a part of the mesh, ClickData
only returns the coordinates of the closest vertex, instead of the actual 3D coordinates of the point that was clicked on. I would need the exact coordinates of the point to find the barycentric coordinates of that point.
My problem is very similar to this (except that I’m using obj format).
Is there a way to get the 3D coordinates of the point being clicked on, instead of the nearest vertex? Or better yet, is there a way to directly obtain the barycentric coordinates (along with the face index) of that point?
Hey @ac13, could you provide a MRE for this?
I’ve done something similar a while ago, but I think your issues might be caused by the watertightness.
Hello,
Thank you for the reply.
This is the Dash callback:
args = [
Output("thing", "figure"),
Input("thing", "clickData"),
State("thing", "figure")]
@self.app.callback(*args)
def get_coords(click_data, fig):
vertex_id = click_data["points"][0]["pointNumber"]
It is here that I want the exact 3D coordinates on the mesh surface. Currently, the data from clickData
gives the coordinates of the closest vertex
The element:
dcc.Graph(
id='thing',
figure=self.create_fig(obj="thing"))
The create_fig
function:
def create_fig(self, colors: Sequence[int] = None, camera: CameraType = None) -> FigureType:
colors = colors or []
return go.Figure(
data=self.thing.mesh_3d(colors),
layout=self.create_thing_layout(camera))
The thing
class:
class Thing:
def __init__(self, mesh_path: PathLike, *_args) -> None:
self._trimesh = trimesh.load(mesh_path, process=False)
self._vertices = np.array(self._trimesh.vertices)
self._faces = np.array(self._trimesh.faces)
@property
def vertices(self) -> ArrayType:
"""The mesh vertices."""
return self._vertices
@property
def faces(self) -> ArrayType:
"""The mesh faces."""
return self._faces
@property
def n_vertices(self) -> int:
"""The number of vertices in the mesh."""
return self._vertices.shape[0]
@property
def n_faces(self) -> int:
"""The number of faces in the mesh."""
return self._faces.shape[0]
@property
def x(self) -> ArrayType:
"""An array of vertex x coordinates"""
return self._vertices[:, 0]
@property
def y(self) -> ArrayType:
"""An array of vertex y coordinates"""
return self._vertices[:, 1]
@property
def z(self) -> ArrayType:
"""An array of vertex z coordinates"""
return self._vertices[:, 2]
@property
def i(self) -> ArrayType:
"""An array of the first face vertices"""
return self._faces[:, 0]
@property
def j(self) -> ArrayType:
"""An array of the second face vertices"""
return self._faces[:, 1]
@property
def k(self) -> ArrayType:
"""An array of the third face vertices"""
return self._faces[:, 2]
@property
def default_selection(self):
"""Default patch selection mask."""
raise NotImplementedError()
def _colors(self, *_args, **_kwargs) -> ArrayType:
return np.tile(
COLOR_DEFAULT,
(self._vertices.shape[0], 1))
def mesh_3d(
self,
selection: Sequence[int],
lighting: Mapping[str, float] = None,
light_position: Mapping[str, int] = None) -> Mesh3d:
lighting = lighting or DEFAULT_LIGHTING
light_position = light_position or DEFAULT_LIGHT_POSITION
return Mesh3d(
x=self.x, y=self.y, z=self.z,
i=self.i, j=self.j, k=self.k,
vertexcolor=self._colors(selection),
lighting=lighting,
lightposition=light_position,
hoverinfo='none')
I hope this will suffice