7

I have a chain of balls. On each ball I have an arrow.

enter image description here

I want to represent the above graphic with plotly,such that I can rotate it, but I am completely lost. How can I position an arrow on the ball?

I started to first 4 arrows as follows:

import plotly.graph_objs as go
import plotly
plotly.offline.init_notebook_mode()


x = [10.1219, 10.42579, 15.21396, 15.42468, 20.29639,20.46268, 25.36298, 25.49156]
y = [5.0545,  5.180104, 5.0545,   5.20337,  5.0545,  5.194271, 5.0545,   5.231627]
z = [5.2713,  5.231409, 5.2713,   5.231409, 5.2713 ,  5.235852,  5.2713, 5.231627]



pairs = [(0,1), (2,3),(4,5), (6,7)]

trace1 = go.Scatter3d(
    x=x,
    y=y,
    z=z,
    mode='markers',
    name='markers'
)

x_lines = list()
y_lines = list()
z_lines = list()


for p in pairs:
    for i in range(2):
        x_lines.append(x[p[i]])
        y_lines.append(y[p[i]])
        z_lines.append(z[p[i]])
    x_lines.append(None)
    y_lines.append(None)
    z_lines.append(None)

trace2 = go.Scatter3d(
    x=x_lines,
    y=y_lines,
    z=z_lines,
    line = dict(width = 2, color = 'rgb(255, 0,0)'))

)

fig = go.Figure(data=[trace1, trace2])
plotly.offline.iplot(fig, filename='simple-3d-scatter')

But instead of lines that connects the pair-points I would like to have arrows.

enter image description here

4
  • 2
    Could you give an error message or something, so we can help you out from there, instead of just giving a problem and saying: "How to solve this?" Please try out some different techniques than just one. You can check out stackoverflow.com/help/how-to-ask Commented Mar 24, 2021 at 21:54
  • I am a beginner with Plotly. I thought I can use a similar method like add_trace to connect points in the 3d plot with arrows. Then instead of using the mode "lines" I thought I could use the mode "arrows". But it seems there is no such function. I try to reformulate my question. Commented Mar 24, 2021 at 22:02
  • 2
    I think that's a bit harsh, @lukas. The poster made a reasonable effort to solve the problem, and Plotly isn't the easiest library to use. In this instance there isn't going to be an error message but the method used to create arrows isn't going to give the poster the chart they want - probably an annotation will work as intended. Commented Mar 25, 2021 at 2:50
  • 1
    Hey @DerekO. You are right :). Thanks, man, I will reconsider and think about you next time, I see something, where people need help, and it looks like a quiz question -ish :) Have a great day. Commented Mar 25, 2021 at 19:34

1 Answer 1

16

My first thought was that you could use annotations to draw arrows as a workaround, but annotations can only draw arrows in x and y dimensions, as described in answers to a similar question. Plotly has not implemented annotation arrows where you can specify all three dimensions.

However, @Abdul Saboor suggested a clever hack which is to draw a cone at the end of your line segments using a basic cone plot. The parameters x, y, z give you the starting location for the base of the cone, and the parameters u, v, w give you the vector field for the direction that the cone will point in.

Therefore, for each pair of points, we can set the starting point for the base of the cone to be, say 95% of the way between the starting and ending ball, and the direction of the cone will be the vector pointing from the starting to the ending points, and the magnitude can be whatever you like, say 10% of the distance between the points so the cone is small and looks nice. You can adjust these using arrow_tip_ratio and arrow_starting_ratio that I set to these arbitrary values.

EDIT: to plot only the starting point and not the ending point, you can modify trace1 by accessing only the first x, y, z coordinate for each starting and ending ball using a list comprehension. Then you should specify the parameter mode='lines' for trace2 so that you only plot the line over the first of each starting and ending point.

import plotly.graph_objs as go
# plotly.offline.init_notebook_mode()

x = [10.1219, 10.42579, 15.21396, 15.42468, 20.29639,20.46268, 25.36298, 25.49156]
y = [5.0545,  5.180104, 5.0545,   5.20337,  5.0545,  5.194271, 5.0545,   5.231627]
z = [5.2713,  5.231409, 5.2713,   5.231409, 5.2713 ,  5.235852,  5.2713, 5.231627]

pairs = [(0,1), (2,3),(4,5), (6,7)]

## plot ONLY the first ball in each pair of balls
trace1 = go.Scatter3d(
    x=[x[p[0]] for p in pairs],
    y=[y[p[0]] for p in pairs],
    z=[z[p[0]] for p in pairs],
    mode='markers',
    name='markers',
    line=dict(color='red')
)

x_lines = list()
y_lines = list()
z_lines = list()

for p in pairs:
    for i in range(2):
        x_lines.append(x[p[i]])
        y_lines.append(y[p[i]])
        z_lines.append(z[p[i]])
    x_lines.append(None)
    y_lines.append(None)
    z_lines.append(None)

## set the mode to lines to plot only the lines and not the balls/markers
trace2 = go.Scatter3d(
    x=x_lines,
    y=y_lines,
    z=z_lines,
    mode='lines',
    line = dict(width = 2, color = 'rgb(255, 0,0)')
)

fig = go.Figure(data=[trace1, trace2])

arrow_tip_ratio = 0.1
arrow_starting_ratio = 0.98

## the cone will point in the direction of vector field u, v, w 
## so we take this to be the difference between each pair 

## then hack the colorscale to force it to display the same color
## by setting the starting and ending colors to be the same

for p in pairs:
    fig.add_trace(go.Cone(
        x=[x[p[0]] + arrow_starting_ratio*(x[p[1]] - x[p[0]])],
        y=[y[p[0]] + arrow_starting_ratio*(y[p[1]] - y[p[0]])],
        z=[z[p[0]] + arrow_starting_ratio*(z[p[1]] - z[p[0]])],
        u=[arrow_tip_ratio*(x[p[1]] - x[p[0]])],
        v=[arrow_tip_ratio*(y[p[1]] - y[p[0]])],
        w=[arrow_tip_ratio*(z[p[1]] - z[p[0]])],
        showlegend=False,
        showscale=False,
        colorscale=[[0, 'rgb(255,0,0)'], [1, 'rgb(255,0,0)']]
        ))

fig.show()

# plotly.offline.iplot(fig, filename='simple-3d-scatter')

![enter image description here

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you very much for the answer. Is it correct that it is not possible to remove the marker points on one side (for example with opacity=0.01) of the connection-line, since one can just modify the line and the markers of the scatter plot at the same time.
Since you are plotting the starting and ending markers in trace1 and the lines in trace2, you can instead plot only the starting markers in trace1. I'll modify my answer when I have a moment
I have updated my answer to only show the starting marker and the line to the ending marker, but no ending marker. Let me know if this looks good!
Wow! Thank you so much for your help and your time. Now it is perfect!! All the best for you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.